# Display output within the notebook
# knitr::opts_chunk$set(echo = TRUE, results = 'show')

Intro to Regression

For more on regression, see LSR.

Using X to predict Y

Is there an association between X and Y?

A scatterplot visualizes the relationship between two quantitative variables on the same individuals.

age <- c(1, 2, 2, 2, 2, 3, 4, 4, 5, 5)
cost <- c(350, 370, 480, 520, 590, 550, 750, 800, 790, 950)

plot(age, cost, pch = 16, las = 1, cex = 1, xlab = "Age of Bus (years)", ylab = "Annual Maintenance Cost ($)")


#From Camm, et. al., Business Analytics, 3e, 356

# A *time series* is a scatterplot with time (seconds, months, years, decades) on the x-axis (as the explanatory variable).

Outcome = Model + Error

Avoid “overfitting”; don’t play connect the dots!

Introductory, baseline ML models

Model Data
Linear regression One predictor with a continuous outcome
Multiple regression Multiple predictors with a continuous outcome
Logistic regression One predictor with a binary outcome
Multiple logistic regression Multiple predictors with a binary outcome
Polynomial regression Relationship between the predictor(s) and the continuous outcome is nonlinear
…and more…

A response variable (y) measures an outcome of a study.

An explanatory variable (x) may help predict or explain changes in a response variable.

If knowing the value of one variable helps us predict the value of the other, there is an association between the two variables.

Regressing Y on X

# mtcars is built-in data within R

plot(mtcars$hp, mtcars$mpg, pch = 16, las = 1, cex = 1, main = "Terminology Practice", xlab = "Horsepower", ylab = "Miles Per Gallon")


Your Turn

The dataset comes from a national veterans’ organization that frequently solicits donations through direct mail campaigns to its database of current and prospective donors. The organization sent out a test mailing to a group of potential donors and gathered information on the response to that test mailing.

From Practical Machine Learning with R by Nwanganga and Chapple (p. 166-68)

Download the data

Download the donors.csv and this Rmd notebook from Brightspace.

Remember to save the csv in the same folder as the Rmd notebook; otherwise, you will need to set your working directory. See previous module for video assistance.

Load the data

# Load the data as "donors"

donors <- read.csv("donors - Copy.csv")

Familiarize yourself with the data

summary(donors)
      age        numberChildren   incomeRating    wealthRating   mailOrderPurchases totalGivingAmount  numberGifts      smallestGiftAmount
 Min.   : 1.00   Min.   :1.000   Min.   :1.000   Min.   :0.000   Min.   :  0.000    Min.   :  13.0    Min.   :  1.000   Min.   :   0.000  
 1st Qu.:48.00   1st Qu.:1.000   1st Qu.:2.000   1st Qu.:3.000   1st Qu.:  0.000    1st Qu.:  40.0    1st Qu.:  3.000   1st Qu.:   3.000  
 Median :62.00   Median :1.000   Median :4.000   Median :6.000   Median :  0.000    Median :  78.0    Median :  7.000   Median :   5.000  
 Mean   :61.61   Mean   :1.528   Mean   :3.886   Mean   :5.346   Mean   :  3.321    Mean   : 104.5    Mean   :  9.602   Mean   :   7.934  
 3rd Qu.:75.00   3rd Qu.:2.000   3rd Qu.:5.000   3rd Qu.:8.000   3rd Qu.:  3.000    3rd Qu.: 131.0    3rd Qu.: 13.000   3rd Qu.:  10.000  
 Max.   :98.00   Max.   :7.000   Max.   :7.000   Max.   :9.000   Max.   :241.000    Max.   :9485.0    Max.   :237.000   Max.   :1000.000  
 NA's   :23665   NA's   :83026   NA's   :21286   NA's   :44732                                                                            
 largestGiftAmount averageGiftAmount  yearsSinceFirstDonation monthsSinceLastDonation inHouseDonor    plannedGivingDonor sweepstakesDonor
 Min.   :   5      Min.   :   1.286   Min.   : 0.000          Min.   : 0.00           Mode :logical   Mode :logical      Mode :logical   
 1st Qu.:  14      1st Qu.:   8.385   1st Qu.: 2.000          1st Qu.:12.00           FALSE:88709     FALSE:95298        FALSE:93795     
 Median :  17      Median :  11.636   Median : 5.000          Median :14.00           TRUE :6703      TRUE :114          TRUE :1617      
 Mean   :  20      Mean   :  13.348   Mean   : 5.596          Mean   :14.36                                                              
 3rd Qu.:  23      3rd Qu.:  15.478   3rd Qu.: 9.000          3rd Qu.:17.00                                                              
 Max.   :5000      Max.   :1000.000   Max.   :13.000          Max.   :23.00                                                              
                                                                                                                                         
  P3Donor           state            urbanicity        socioEconomicStatus isHomeowner       gender          respondedMailing
 Mode :logical   Length:95412       Length:95412       Length:95412        Mode:logical   Length:95412       Mode :logical   
 FALSE:93395     Class :character   Class :character   Class :character    TRUE:52354     Class :character   FALSE:90569     
 TRUE :2017      Mode  :character   Mode  :character   Mode  :character    NA's:43058     Mode  :character   TRUE :4843      
                                                                                                                             
                                                                                                                             
                                                                                                                             
                                                                                                                             

Summarize data



# Note some weird features!
# See ages, numberChildren, lots of NAs
# We need a codebook!

Sampling from the Population

Sample: 50 donors (n = 50)

Population: All donors (N = 95,412)

# For reproducibility
set.seed(1)

# Randomly select 50 numbers to represent rows in dataframe
sampleIDs <- sample(1:95412, size = 50, replace = FALSE)

# Select the columns I'm interested in
myColumns <- c("age", "incomeRating", "totalGivingAmount", "largestGiftAmount", "isHomeowner", "gender", "urbanicity", "respondedMailing")

# Store the randomly selected rows on the given columns
sampleDonors <- donors[sampleIDs, myColumns]
sampleDonors
NA

Review correlation matrix

With NAs

cor(sampleDonors[1:50, na.rm=FALSE, c("age", "incomeRating", "totalGivingAmount", "largestGiftAmount")])
G1;H1;Errorh in `[.data.frame`(sampleDonors, 1:50, na.rm = FALSE, c("age", "incomeRating",  : 
  unused argument (na.rm = FALSE)
Error during wrapup: not that many frames on the stack
Error: no more error handlers available (recursive errors?); invoking 'abort' restart
g
?cor

Removing NAs

round(cor(sampleDonors[1:50, c("age", "incomeRating", "totalGivingAmount", "largestGiftAmount")], use = "complete.obs"), 2)
                    age incomeRating totalGivingAmount largestGiftAmount
age                1.00        -0.29              0.13             -0.20
incomeRating      -0.29         1.00              0.11              0.32
totalGivingAmount  0.13         0.11              1.00              0.54
largestGiftAmount -0.20         0.32              0.54              1.00

A Scatterplot

plot(sampleDonors$age, 
     sampleDonors$incomeRating, 
     cex = 0.5,
     las = 1,
     pch = 16, 
     xlab = "Age", 
     ylab = "Income Rating")


The Line of Best Fit

*Be sure you finished the task in the prior video!

outcome = model + error

giftCor <- round(cor(sampleDonors$largestGiftAmount, sampleDonors$totalGivingAmount), 2)

plot(sampleDonors$largestGiftAmount, 
     sampleDonors$totalGivingAmount, 
     cex = 1.5,
     las = 1,
     pch = 16, 
     xlab = "Largest Gift Amount", 
     ylab = "Total Giving",
     main = paste("r = ", giftCor))

Review: Describe the relationship between the largest gift amount and total giving for this sample of 50 donors.

What line best models this relationship?

  • “Trend line”

  • “Line of Best Fit”

  • “Least Squares Regression Line” (LSRL)

  • “Ordinary Least Squares Regression” (OLSR)

lm(y ~ x) = linear model predicting y given x

# Calculate coefficients of best fitting linear model

model <- lm(sampleDonors$totalGivingAmount ~ sampleDonors$largestGiftAmount)
##response first (Y), predictor (x) 

##51.82 is the coeff, y intercept , touches the y axis


##3.91 is the slope of the line

model

Call:
lm(formula = sampleDonors$totalGivingAmount ~ sampleDonors$largestGiftAmount)

Coefficients:
                   (Intercept)  sampleDonors$largestGiftAmount  
                         51.82                            3.91  

Intercept is the y-intercept, where the best-fitting line hits the y-axis

Slope is the “incline” of the best-fitting line

“Slope-Intercept Form”

\[ y = mx + b \]

m is the slope b is the y-intercept

In statistics, we write it differently: \[ \widehat{y} = a + b(x) \\ \widehat{y} = a + bx \\ \widehat{y} = b_0 + b_1x \]

The latter form makes the most sense, given multiple regression (when we have multiple coefficients, not just two).

\(b_0\) and \(b_1\) are parameters (also as \(\theta_0\) and \(\theta_1\) or \(\beta_0\) and \(\beta_1\))


Why the hat \(\hat{y}\)? Why is it important?

Outcome = Model + Error

\[y = a + bx + \epsilon \\\] or \[\hat{y} = a + bx \\\]

It’s a trend, not a guarantee; not “deterministic”, not “exact”

lm(y ~ x)$coefficients[1] to access the y-intercept

lm(y ~ x)$coefficients[2] to access the slope

# Store the coefficients on the linear model
my_intercept <- round(model$coefficients[1], 1)
my_slope <- round(model$coefficients[2], 1)

# Plot scatterplot
plot(sampleDonors$largestGiftAmount, 
     sampleDonors$totalGivingAmount, 
     cex = 1.5,
     las = 1,
     pch = 16, 
     xlab = "Largest Gift Amount", 
     ylab = "Total Giving",
     main = paste("LSRL: ", "Predicted y = ", my_intercept, "+", my_slope, "(x)"))

# Plot LSRL
abline(model, 
       lwd = 2,        # make the line wider
       col = "red"     # color the line
       )


Residuals

Before we can understand what makes the LSRL the “best fitting” line, we need to understand what a residual is.

Let’s consider the scatterplot:

# Store the coefficients on the linear model
my_intercept <- round(model$coefficients[1], 1)
my_slope <- round(model$coefficients[2], 1)

# Plot scatterplot
plot(sampleDonors$largestGiftAmount, 
     sampleDonors$totalGivingAmount, 
     cex = 1.5,
     las = 1,
     pch = 16, 
     xlab = "Largest Gift Amount", 
     ylab = "Total Giving",
     main = paste("LSRL: ", "Predicted y = ", my_intercept, "+", my_slope, "(x)"))

# Plot LSRL
abline(model, 
       lwd = 2,        # make the line wider
       col = "red"     # color the line
       )


A residual is the difference between the actual value of y and the value of y predicted by the regression line.

Residual = actual y - predicted y

or

Residual = observed y - expected y

or

\[ \epsilon = y - \hat{y} \]

A positive residual means…

A negative residual means…

# Create a simple dataset
x <- c(1, 2, 3, 4, 5)
y <- c(2, 4, 6, 8, 10)  # Perfectly linear relationship (y = 2x)

# Introduce one positive residual and one negative residual
y[2] <- y[2] + 2  # Positive residual
y[4] <- y[4] - 2  # Negative residual

# Fit a linear model
ex_model <- lm(y ~ x)

# Plot the data and the fitted line
plot(x, y, pch = 16, cex = 1.5, las = 1, col = "black", main = "Positive & Negative Residuals")
abline(ex_model, col = "blue", lwd = 3)

# Add residual lines
for (i in 1:length(x)) {
  lines(c(x[i], x[i]), c(y[i], predict(ex_model)[i]), col = "red", lty = 2, lwd = 2.5)
}

“the line underpredicts when…”

“the line overpredicts when…”

If a residual is close to (or equals) zero, that means…

The sum of all the residuals = __.

Note: A residual is the vertical distance from a point to the LSRL

# Find the donor who gave $150 as largest gift amount
sampleDonors[sampleDonors$largestGiftAmount == 150, ]

# Alternate approach
which(sampleDonors$largestGiftAmount == 150)
[1] 46
### 46 is the 46th row in sampleDonors and does not match the original index of 13602
# Pick select columns
sampleDonors[sampleDonors$largestGiftAmount == 150, c("largestGiftAmount", "totalGivingAmount")]

Calculate and interpret the residual for this donor.

190 - (51.8 + 3.9* (150))
[1] -446.8
## 

Interpretation: for the is doner whos largetst gitf was 150, the lsrl overpred their total giving my 446

or this donor gave 446 less than predicted.


Practice

Find the donor in this sample whose largest gift amount was $100, and then calculate and interpret the residual for this donor.

which(sampleDonors$largestGiftAmount==100)
[1] 5
sampleDonors[sampleDonors$largestGiftAmount == 100, c("largestGiftAmount", "totalGivingAmount")]
#which(sampleDonors$largestGiftAmount == 150)
#190 - (51.8 + 3.9* (150))

921- (51.8 + 3.9(100))
G1;H1;Errorh: attempt to apply non-function
Error during wrapup: not that many frames on the stack
Error: no more error handlers available (recursive errors?); invoking 'abort' restart
g

Interpretation:

R stores the residuals

lm(y ~ x)$residuals

# List all residuals
model$residuals
            1             2             3             4             5             6             7             8             9            10 
 -41.18416812 -147.29733501  -41.91299372 -107.38203262  478.22223837  -30.00907904  -66.91299372   14.08700628  -58.91868757  139.49092096 
           11            12            13            14            15            16            17            18            19            20 
 -45.20124969  -64.00907904    2.08700628  -95.46103638  -25.65320703   -0.00907904 -124.55712170  -88.37064491    9.63504895  -83.00907904 
           21            22            23            24            25            26            27            28            29            30 
 -36.73221078  -29.55712170   23.63504895  -78.55712170  -58.91299372  132.99092096  165.03896362  -27.46103638   61.44287830  140.44287830 
           31            32            33            34            35            36            37            38            39            40 
  97.71974656  296.08971117   45.26778922  300.41440901  -43.73221078  -62.28025344  -62.28025344   58.99661482  246.71974656  -73.46103638 
           41            42            43            44            45            46            47            48            49            50 
  -1.27455959   65.70266499 -124.55712170  -51.09377665   32.08700628 -448.25818826  -50.00907904  -20.18986197  -59.91299372  -61.91299372 
# Order the residuals
#sort(model$residuals)

Illustrating residuals

# Create linear regression model
model <- lm(sampleDonors$totalGivingAmount ~ sampleDonors$largestGiftAmount)

# Plot scatterplot
plot(sampleDonors$largestGiftAmount, 
     sampleDonors$totalGivingAmount, 
     cex = 1.5,
     las = 1,
     pch = 16, 
     xlab = "Largest Gift Amount", 
     ylab = "Total Giving",
     main = paste("LSRL: ", "y-hat = ", my_intercept, "+", my_slope, "(x)"))

# Plot LSRL
abline(model, 
       lwd = 2,        # make the line wider
       col = "red"     # color the line
       )

# Plot each residual from y to predicted y (i.e. fitted value) with segments()
## segments(x1, y1, x2, y2)

segments(sampleDonors$largestGiftAmount, # x1
         fitted(model),                   # y1
         sampleDonors$largestGiftAmount, # x2
         sampleDonors$totalGivingAmount,  # y2
         col = "blue",
         lty = 2) 


Residual Plot

We can plot the explanatory variable (or the predictions) vs. the residuals on a plot called a residual plot. More on residual plots later.

# Plot scatterplot with LSRL
plot(sampleDonors$largestGiftAmount, 
     sampleDonors$totalGivingAmount, 
     cex = 1.5,
     las = 1,
     pch = 16, 
     xlab = "Largest Gift Amount", 
     ylab = "Total Giving",
     main = "Scatterplot")
abline(model, 
       lwd = 2,        # make the line wider
       col = "red"     # color the line
       )


# Residual Plot
plot(sampleDonors$largestGiftAmount, 
     model$residuals, 
     xlab = "Largest Gift Amount", 
     ylab = "Residual", 
     las = 1,
     pch = 16,
     cex = 1.5,
     main = "Residual Plot")

abline(h = 0, col = "red", lwd = 2)

R comes with a built-in residual plot (which includes other diagnostic graphs).

plot(model)


Why is the least-squares regression line the “best fitting” line?

Squared Residuals

Another applet: Guess the LSRL Applet

The least-squares regression is the best-fitting line because it ___ the ___ of the squared ___.

\[ d_1^2+d_2^2+d_3^2+d_4^2+ ...+d_n^2= \mbox{a minimum} \]

\[ e_1^2+e_2^2+e_3^2+e_4^2+ ...+e_n^2= \mbox{a minimum} \]

\[ \underset{b_0, b_1}{\text{min}} \mbox{ }\Sigma(residuals)^2 \]

\[ \underset{b_0, b_1}{\text{min}} \mbox{ }\Sigma(y-b_0-b_1x)^2 \]


How are the coefficients of the LSRL calculated?

The slope

  1. The slope using averages:

\[ b = \frac{\sum_{i=1}^n (x_i - \bar{x})(y_i - \bar{y})}{\sum_{i=1}^n (x_i - \bar{x})^2} \]

  1. The slope using sums only: \[ b = \displaystyle\frac{\displaystyle\ n \sum x_i y_i - \sum x_i \sum y_i}{\displaystyle\ n \sum x_i^2 - (\sum x_i)^2} \]

To derive these formulas, we need multivariate calculus and linear algebra.

  1. The slope using correlation \[ b = r\frac{s_y}{s_x}\]

The y-intercept

\(\hat{y} = a + b(x)\)

The centroid \((\bar{x}, \bar{y})\) always lies on the LSRL.

\[ a = \bar{y} - b(\bar{x}) \]


Identify the coefficients in regression output

# Note different syntax with data = ...
model <- lm(totalGivingAmount ~ largestGiftAmount, data = sampleDonors)
#totalGiving Amount (response) (X) ~ largest explanatory (y)
model

Call:
lm(formula = totalGivingAmount ~ largestGiftAmount, data = sampleDonors)

Coefficients:
      (Intercept)  largestGiftAmount  
            51.82               3.91  
summary(model)

Call:
lm(formula = totalGivingAmount ~ largestGiftAmount, data = sampleDonors)

Residuals:
    Min      1Q  Median      3Q     Max 
-448.26  -62.28  -33.37   41.97  478.22 

Coefficients:
                  Estimate Std. Error t value Pr(>|t|)    
(Intercept)         51.817     27.292   1.899   0.0636 .  
largestGiftAmount    3.910      0.782   5.000 8.07e-06 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 138.8 on 48 degrees of freedom
Multiple R-squared:  0.3424,    Adjusted R-squared:  0.3287 
F-statistic:    25 on 1 and 48 DF,  p-value: 8.075e-06

Identify the slope and the y-intercept in the regression output above.


Interpreting the Coefficients of a Linear Model

model <- lm(totalGivingAmount ~ largestGiftAmount, data = sampleDonors)
G1;H1;Errorh in eval(mf, parent.frame()) : object 'sampleDonors' not found
Error during wrapup: not that many frames on the stack
Error: no more error handlers available (recursive errors?); invoking 'abort' restart
g
model
G1;H1;Errorh: object 'model' not found
Error during wrapup: not that many frames on the stack
Error: no more error handlers available (recursive errors?); invoking 'abort' restart
g
### NOTE: 
### model <- lm(sampleDonors$totalGivingAmount ~ sampleDonors$largestGiftAmount) 
### also works here but creates an issue with predict() below

# Store the coefficients on the linear model
my_intercept <- round(model$coefficients[1], 1)
G1;H1;Errorh: object 'model' not found
Error during wrapup: not that many frames on the stack
Error: no more error handlers available (recursive errors?); invoking 'abort' restart
g
print("Intercept")
[1] "Intercept"
my_slope <- round(model$coefficients[2], 1)
G1;H1;Errorh: object 'model' not found
Error during wrapup: not that many frames on the stack
Error: no more error handlers available (recursive errors?); invoking 'abort' restart
g
print("Slope")
[1] "Slope"
# Plot scatterplot
plot(sampleDonors$largestGiftAmount, 
     sampleDonors$totalGivingAmount, 
     cex = 1.5,
     las = 1,
     pch = 16, 
     xlab = "Largest Gift Amount", 
     ylab = "Total Giving",
     main = paste("LSRL: ", "Predicted y = ", my_intercept, "+", my_slope, "(x)"))
G1;H1;Errorh: object 'sampleDonors' not found
Error during wrapup: not that many frames on the stack
Error: no more error handlers available (recursive errors?); invoking 'abort' restart
g
# Plot LSRL
abline(model, 
       lwd = 2,        # make the line wider
       col = "red"     # color the line
       )
G1;H1;Errorh: object 'model' not found
Error during wrapup: not that many frames on the stack
Error: no more error handlers available (recursive errors?); invoking 'abort' restart
g

The slope is a rate of change.

\[ slope = \frac{rise}{run} = \frac{\Delta y}{\Delta x} = \frac{y_2-y_1}{x_2-x_1} = \frac{slope}{1} \]

To interpret the slope:


\[ Y-intercept: (0, y) \]

To interpret the y-intercept:


Interpret the slope and y-intercept of our linear model:

model$coefficients
      (Intercept) largestGiftAmount 
        51.816908          3.909609 

Slope: We predict the total giving to increase $3.91 for each additional $1 increase in largest gift amount.

Y-intercept: When a donor’s largest gift amount is $0, the predicted total giving is $51.82.


Final Comments

1. The slope is a prediction or estimate; it’s not a guarantee, so we must use “predicted” in our interpretation.

NOT THIS: “The total giving will increase $3.90 for every $1 increase in highest gift amount”

2. The y-intercept often does not have a meaningful interpretation.

“We predict a resting heart rate of -57 bpm for a body weight of 0 pounds.”

3. Beware of extrapolation

Making a prediction outside the domain of our explanatory variable.

Use the LSRL to predict the total giving of a donor whose largest given was $5000.

model

Call:
lm(formula = totalGivingAmount ~ largestGiftAmount, data = sampleDonors)

Coefficients:
      (Intercept)  largestGiftAmount  
            51.82               3.91  
51.82 + 3.91*5000
[1] 19601.82
predict(model, data.frame(largestGiftAmount = 5000))
       1 
19599.86 
plot(sampleDonors$largestGiftAmount, 
     sampleDonors$totalGivingAmount, 
     cex = 1.5,
     las = 1,
     pch = 16, 
     xlab = "Largest Gift Amount", 
     ylab = "Total Giving",
     main = paste("What is extrapolation?"))

# Plot LSRL
abline(model, 
       lwd = 2,        # make the line wider
       col = "red"     # color the line
       )


Residual Plots: Is the Model Appropriate?

Example 1

position <- 1:10
rate <- c(23.53, 14.94, 11.19, 7.47, 5.29, 3.8, 2.79, 2.11, 1.57, 1.18)

# Create scatterplot
plot(position,                    # variable on x-axis
     rate,                        # variable on y-axis
     pch = 16,                    # filled-in circles
     cex = 2,                     # larger circle size
     las = 1,                     # rotate numbers on y-axis
     xlab = "Position",           # x-axis label
     ylab = "Click-through Rate") # y-axis label

abline(rate_model, col="red", 2)

# 1) Find the linear model for position vs. rate

rate_model <- lm (rate ~ position)
rate_model

Call:
lm(formula = rate ~ position)

Coefficients:
(Intercept)     position  
     19.243       -2.156  
# 2) Construct a residual plot (use position vs. residuals).

rate_model$residuals
           1            2            3            4            5            6            7            8            9           10 
 6.442909091  0.008484848 -1.585939394 -3.150363636 -3.174787879 -2.509212121 -1.363636364  0.111939394  1.727515152  3.493090909 
plot (position, rate_model$residuals, pch=16)
# 3) Include a horizontal line at residual = 0.

abline(h=0, col="red")

  • If the residual plot shows a curved pattern or a fanning out/in shape, the model was __curved_____. not appropriate

  • If the residual plot shows random scatter, the model was appropriate.

The model should explain the pattern; if there’s a left-over pattern, try a different model (e.g., quadratic)


Example 2

model <- lm(totalGivingAmount ~ largestGiftAmount, data = sampleDonors)

plot(sampleDonors$largestGiftAmount, 
     model$residuals, 
     xlab = "Largest Gift Amount", 
     ylab = "Residual", 
     las = 1,
     pch = 16,
     cex = 1.5)

abline(h = 0, col = "red", lwd = 2)

Example 3

Is a linear model an appropriate model for the relationships between age and largest gift amount?

plot(sampleDonors$age, sampleDonors$largestGiftAmount)

# Create linear model
model_age <- lm(sampleDonors$largestGiftAmount ~ sampleDonors$age)

# Plot model diagnostics
plot(model_age)


For more on residual plots, see LSR.

R-Squared: How Well Does the Model Fit?

Residual plots: did I pick an appropriate model?

R-Squared: does the model do a good job?

For more on r-squared, see LSR.

Comparing SSE with SST


# Have both plots appear in one viewing window (1 row, 2 columns)
par(mfrow = c(1, 2))

# Plot 1
plot(sampleDonors$largestGiftAmount, 
     sampleDonors$totalGivingAmount, 
     cex = 1,
     las = 1,
     pch = 16, 
     xlab = "Largest Gift Amount", 
     ylab = "Total Giving", 
     main = "Should we use the mean?")

# Plot mean response
abline(h = mean(sampleDonors$totalGivingAmount), lwd = 2, col = "blue")

# Plot 2
plot(sampleDonors$largestGiftAmount, 
     sampleDonors$totalGivingAmount, 
     cex = 1,
     las = 1,
     pch = 16, 
     xlab = "Largest Gift Amount", 
     ylab = "Total Giving",
     main = "Should we use the model?")

# Plot LSRL
abline(model, 
       lwd = 2,        # make the line wider
       col = "red"     # color the line
       )

Which model should we use to make predictions: the mean of the response or the LSRL?


Calculate SST

# View response variable values
sampleDonors$totalGivingAmount
 [1]  38.0 100.0  49.0  93.0 921.0 100.0  24.0 105.0  75.0 269.5 163.0  66.0  93.0  15.0 163.0 130.0  25.0  26.0  81.0  47.0  62.0 120.0  95.0  71.0
[25]  32.0 263.0 275.5  83.0 211.0 290.0 216.0 503.9 144.0 665.0  55.0  56.0  56.0 146.0 365.0  37.0  74.0 313.0  25.0  32.0 123.0 190.0  80.0 102.0
[49]  31.0  29.0
# Find deviation from point to the mean response
totGivingDev <- sampleDonors$totalGivingAmount - mean(sampleDonors$totalGivingAmount)
totGivingDev
 [1] -108.578  -46.578  -97.578  -53.578  774.422  -46.578 -122.578  -41.578  -71.578  122.922   16.422  -80.578  -53.578 -131.578   16.422  -16.578
[17] -121.578 -120.578  -65.578  -99.578  -84.578  -26.578  -51.578  -75.578 -114.578  116.422  128.922  -63.578   64.422  143.422   69.422  357.322
[33]   -2.578  518.422  -91.578  -90.578  -90.578   -0.578  218.422 -109.578  -72.578  166.422 -121.578 -114.578  -23.578   43.422  -66.578  -44.578
[49] -115.578 -117.578
# Square the deviations
totGivingDev^2
 [1]  11789.182084   2169.510084   9521.466084   2870.602084 599729.434084   2169.510084  15025.366084   1728.730084   5123.410084  15109.818084
[11]    269.682084   6492.814084   2870.602084  17312.770084    269.682084    274.830084  14781.210084  14539.054084   4300.474084   9915.778084
[21]   7153.438084    706.390084   2660.290084   5712.034084  13128.118084  13554.082084  16620.882084   4042.162084   4150.194084  20569.870084
[31]   4819.414084 127679.011684      6.646084 268761.370084   8386.530084   8204.374084   8204.374084      0.334084  47708.170084  12007.338084
[41]   5267.566084  27696.282084  14781.210084  13128.118084    555.922084   1885.470084   4432.630084   1987.198084  13358.274084  13824.586084
# Calculate total sum of squares
sst <- sum(totGivingDev^2)

sst
[1] 1407256

Calculate SSE

# Find deviation from point to LSRL
model$residuals
        24388         59521         43307         69586         11571         25173         32618         13903          8229         25305 
 -41.18416812 -147.29733501  -41.91299372 -107.38203262  478.22223837  -30.00907904  -66.91299372   14.08700628  -58.91868757  139.49092096 
        90597         22306         12204         43809         72611         92490         36244         45399         81580          6519 
 -45.20124969  -64.00907904    2.08700628  -95.46103638  -25.65320703   -0.00907904 -124.55712170  -88.37064491    9.63504895  -83.00907904 
        92199         19242         87320         82446         21875         58472         91095         62956         21323         13284 
 -36.73221078  -29.55712170   23.63504895  -78.55712170  -58.91299372  132.99092096  165.03896362  -27.46103638   61.44287830  140.44287830 
         7976          9392          3863         52253         26876         88684         13973         31334         39241         47959 
  97.71974656  296.08971117   45.26778922  300.41440901  -43.73221078  -62.28025344  -62.28025344   58.99661482  246.71974656  -73.46103638 
        28278         66394         72299         11367         95283         13602          5051         16920         29660         56659 
  -1.27455959   65.70266499 -124.55712170  -51.09377665   32.08700628 -448.25818826  -50.00907904  -20.18986197  -59.91299372  -61.91299372 
# Square the deviations
(model$residuals)^2
             24388              59521              43307              69586              11571              25173              32618              13903 
  1696.13570357291  21696.50490175439   1756.69904217855  11530.90093030444 228696.50926800512    900.54482480329   4477.34872794341    198.44374606526 
              8229              25305              90597              22306              12204              43809              72611              92490 
  3471.41174520692  19457.71703038830   2043.15297336797   4097.16219949421      4.35559523239   9112.80946625392    658.08703071803      0.00008242896 
             36244              45399              81580               6519              92199              19242              87320              82446 
 15514.47656661454   7809.37088178496     92.83416820801   6890.50720299795   1349.25530879753    873.62344328968    558.61553871954   6171.22137005724 
             21875              58472              91095              62956              21323              13284               7976               9392 
  3470.74082849865  17686.58505790274  27237.85951361042    754.10851892279   3775.22729357851  19724.20206470836   9549.14886730312  87669.11705784447 
              3863              52253              26876              88684              13973              31334              39241              47959 
  2049.17274085257  90248.81714369814   1912.50625971966   3878.82996883584   3878.82996883584   3480.60055988084  60870.63334150079   5396.52386564679 
             28278              66394              72299              11367              95283              13602               5051              16920 
     1.62450213721   4316.84018647315  15514.47656661454   2610.57401240528   1029.57597231456 200935.40333778711   2500.90798638618    407.63052655813 
             29660              56659 
  3589.56681592925   3833.21879079043 
# Calculate sum of squares when using the model
sse <- sum((model$residuals)^2)
sse
[1] 925380.4

Note: I used SSE here, but SSM or SSR or SSresid are other names for the same quantity.

Calculate the proportion of error that remains

1 - sse/sst
[1] 0.3424222

Calculate the proportion of error that the LSRL removed

# Find the correlation
giving_correlation <- cor(sampleDonors$largestGiftAmount, sampleDonors$totalGivingAmount)
giving_correlation
[1] 0.5851685
# Square the correlation
giving_correlation^2
[1] 0.3424222

An amazing connection:

# Find the correlation
giving_correlation <- cor(sampleDonors$largestGiftAmount, sampleDonors$totalGivingAmount)
giving_correlation
[1] 0.5851685
# Square the correlation
giving_correlation^2
[1] 0.3424222

\[ r^2 = 1-\frac{SSE}{SST} \]

We want \(r^2\) to be as large as possible (i.e., close to 1.00); we can compare \(r^2\) across various models.

Interpretation

\(r^2\): “the coefficient of determination”

  • Measures of the percent reduction in the sum of squared residuals when using the LSRL to make predictions, rather than using the mean value of y (the response variable)

  • Measures the proportion (or percentage) of the variation in the response variable that is accounted for by the LSRL using the explanatory variable

# Regression output provides R-squared
summary(model)

Interpret \(r^2 = 0.34\) in this context.

  • The LSRL using largest gift amount accounts for 34% of the variability in total giving amount.

  • 34% of the variability in total giving amount is accounted for by the LSRL using largest gift amount.

The linear model using [explanatory variable] accounts for \(r^2\) of the variability in [response variable].


One last example

position <- 1:10
rate <- c(23.53, 14.94, 11.19, 7.47, 5.29, 3.8, 2.79, 2.11, 1.57, 1.18)

par(mfrow = c(1, 2))

plot(position,                   
     rate,                        
     pch = 16,                    
     cex = 2,                     
     las = 1,                     
     xlab = "Position",           
     ylab = "Click-through Rate",
     main = "Should we use the mean?") 
abline(h = mean(rate), lwd = 2, col = "blue")

plot(position,                   
     rate,                        
     pch = 16,                    
     cex = 2,                     
     las = 1,                     
     xlab = "Position",           
     ylab = "Click-through Rate",
     main = "Should we use the model?") 

abline(lm(rate~position), lwd = 2, col = "red")

cor(position, rate)^2

Interpret \(r^2=0.8144\) in context.


Inference for Slope: CI

Use a statistic from a sample to learn about an unknown parameter from a population.

Summary of some parameters and statistics in this course:

Data Type Parameter Statistic
Means Quantitative \(\mu\) \(\bar{x}\)
Difference in Means Quantitative \(\mu_1-\mu_2\) \(\bar{x}_1-\bar{x}_2\)
Proportion Categorical \(p\) \(\widehat{p}\)
Difference in Proportions Categorical \(p_1-p_2\) \(\widehat{p }_1-\widehat{p}_2\)
Slope Quantitative \(\beta\) \(b\) or \(b_1\) or \(\widehat{\beta}_1\)

Confidence Interval: We would like to estimate the true population slope \(\beta\) between two variables using our sample slope \(b\).

Sampling Variability Illustration

# I am not setting a random seed here, so your results will differ from mine.

# Pick 4 different random samples
sampleIDs1 <- sample(1:95412, size = 50, replace = FALSE)
sampleIDs2 <- sample(1:95412, size = 50, replace = FALSE)
sampleIDs3 <- sample(1:95412, size = 50, replace = FALSE)
sampleIDs4 <- sample(1:95412, size = 50, replace = FALSE)

# Select the columns I'm interested in
myColumns <- c("age", "incomeRating", "totalGivingAmount", "largestGiftAmount", "isHomeowner", "gender", "urbanicity", "respondedMailing")

# Store the randomly selected rows on the given columns
sampleDonors1 <- donors[sampleIDs1, myColumns]
sampleDonors2 <- donors[sampleIDs2, myColumns]
sampleDonors3 <- donors[sampleIDs3, myColumns]
sampleDonors4 <- donors[sampleIDs4, myColumns]

# Calculate linear model for each sample
mod1 <- lm(sampleDonors1$totalGivingAmount~sampleDonors1$largestGiftAmount)
mod2 <- lm(sampleDonors2$totalGivingAmount~sampleDonors2$largestGiftAmount)
mod3 <- lm(sampleDonors3$totalGivingAmount~sampleDonors3$largestGiftAmount)
mod4 <- lm(sampleDonors4$totalGivingAmount~sampleDonors4$largestGiftAmount)

# Store the slope from each model
mod1slope <- round(mod1$coefficients[2], 1)
mod2slope <- round(mod2$coefficients[2], 1)
mod3slope <- round(mod3$coefficients[2], 1)
mod4slope <- round(mod4$coefficients[2], 1)

# Store r-squared for each model
mod1corr <- round(sqrt(summary(mod1)$r.squared), 2)
mod2corr <- round(sqrt(summary(mod2)$r.squared), 2)
mod3corr <- round(sqrt(summary(mod3)$r.squared), 2)
mod4corr <- round(sqrt(summary(mod4)$r.squared), 2)

# 2x2 viewing window
par(mfrow = c(2, 2))

# Plot 4 scatterplots with LSRL to illustrate sampling variability

plot(sampleDonors1$largestGiftAmount, sampleDonors1$totalGivingAmount, cex = 1.5, las = 1, pch = 16,  xlab = "Largest Gift",  ylab = "Total Giving", main = paste("b =", mod1slope, "and r =", mod1corr))

abline(mod1, col = "red", lwd = 1.5)

plot(sampleDonors2$largestGiftAmount, sampleDonors2$totalGivingAmount, cex = 1.5, las = 1, pch = 16,  xlab = "Largest Gift",  ylab = "Total Giving", main = paste("b =", mod2slope, "and r =", mod2corr))

abline(mod2, col = "red", lwd = 1.5)

plot(sampleDonors3$largestGiftAmount, sampleDonors3$totalGivingAmount, cex = 1.5, las = 1, pch = 16,  xlab = "Largest Gift",  ylab = "Total Giving", main = paste("b =", mod3slope, "and r =", mod3corr))

abline(mod3, col = "red", lwd = 1.5)

plot(sampleDonors4$largestGiftAmount, sampleDonors4$totalGivingAmount, cex = 1.5, las = 1, pch = 16,  xlab = "Largest Gift",  ylab = "Total Giving", main = paste("b =", mod4slope, "and r =", mod4corr))

abline(mod4, col = "red", lwd = 1.5)


# Note to self: making a function would have been wise here!

Confidence Interval for the Slope of a Population Regression Line

Estimate \(\pm\) Margin of Error

CI for a Population Mean\[\bar{x} \pm t^*_{df}(SEM)\] \[ b \pm t^*_{df}(SE_b) \]

where b is the slope, t* follows a t-distribution with df = n - 2 and \(SE_b\) is the standard error of the slope


Example: Donor Gifts

plot(sampleDonors$largestGiftAmount, 
     sampleDonors$totalGivingAmount, 
     cex = 1,
     las = 1,
     pch = 16, 
     xlab = "Largest Gift Amount", 
     ylab = "Total Giving")

# Plot LSRL
abline(model, 
       lwd = 2,        # make the line wider
       col = "red"     # color the line
       )

# t critical value
tcrit <- qt(p = 0.975, df = 50-2)
tcrit
[1] 2.010635
# Lower bound
3.910 - tcrit*0.782
[1] 2.337684
# Upper bound
3.910 + tcrit*0.782
[1] 5.482316

1. Calculate a 95% confidence interval for the slope of the population regression line.

\[ b \pm t^*_{df}(SE_b) \]

# t critical value
tcrit <- qt(p = 0.975, df = 50-2)
tcrit
[1] 2.010635
# Lower bound
3.910 - tcrit*0.782
[1] 2.337684
# Upper bound
3.910 + tcrit*0.782
[1] 5.482316

2. Use R’s confint(model, level = ...) to verify your answer.

plot(sampleDonors$largestGiftAmount, 
     sampleDonors$totalGivingAmount, 
     pch = 16,
     las = 1,
     xlab = "Largest Gift Amount",
     ylab = "Total Giving")

3. Why might our interval be untrustworthy?

plot(sampleDonors$largestGiftAmount, 
     sampleDonors$totalGivingAmount, 
     pch = 16,
     las = 1,
     xlab = "Largest Gift Amount",
     ylab = "Total Giving")

4. We know the population regression line! (See below.) Did our interval capture the true slope?

# Calculate population regression line
popLine <- lm(donors$totalGivingAmount ~ donors$largestGiftAmount)

# Plot all donors
plot(donors$largestGiftAmount, 
     donors$totalGivingAmount, 
     cex = 1,
     las = 1,
     pch = 16, 
     xlab = "Largest Gift Amount", 
     ylab = "Total Giving",
     main = paste("Population Slope = ", round(popLine$coefficients[2], 2)))

# Plot Population regression line
abline(popLine, 
       lwd = 2,        # make the line wider
       col = "black"     # color the line
       )

# Plot LSRL from sample model
abline(model, 
       lwd = 2,        # make the line wider
       col = "red"     # color the line
       )

# Add a legend
legend("bottomright",              
       legend = c("Population LSRL", "Sample LSRL"), 
       col = c("black", "red"),   
       pch = c(15, 15)) 

We are 95% confident that the interval (2.337, 5.482) captures the population slope of total giving on largest gift amount for all the donors in this organization.

The population slope was 2.64, so yes, our interval captured the parameter.

Inference for Slope: ST

We took a sample of donors from the population of all donors.

# For reproducibility
set.seed(1)

# Randomly select 50 numbers to represent rows in dataframe
sampleIDs <- sample(1:95412, size = 50, replace = FALSE)

# Select the columns I'm interested in
myColumns <- c("age", "incomeRating", "totalGivingAmount", "largestGiftAmount", "isHomeowner", "gender", "urbanicity", "respondedMailing")

# Store the randomly selected rows on the given columns
sampleDonors <- donors[sampleIDs, myColumns]
sampleDonors

We would like to see if the sample slope is statistically significant (i.e., a __ P-value).

Goal for our models: predictors with low p-values and a model with a high \(r^2\)

Perform a test of significance for the population slope at \(\alpha = 0.05\).

\(H_0: \beta = 0\)

\(H_a: \beta \ne 0\)

where \(\beta\) = the slope of the population regression line of total giving on largest gift for all donors in this organization

summary(model)

Call:
lm(formula = totalGivingAmount ~ largestGiftAmount, data = sampleDonors)

Residuals:
    Min      1Q  Median      3Q     Max 
-448.26  -62.28  -33.37   41.97  478.22 

Coefficients:
                  Estimate Std. Error t value Pr(>|t|)    
(Intercept)         51.817     27.292   1.899   0.0636 .  
largestGiftAmount    3.910      0.782   5.000 8.07e-06 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 138.8 on 48 degrees of freedom
Multiple R-squared:  0.3424,    Adjusted R-squared:  0.3287 
F-statistic:    25 on 1 and 48 DF,  p-value: 8.075e-06

Since the p-value is less than the alpha level of 0.05, we have convincing evidence that the slope of the population regression line using largest gift to predict total giving is not zero.

In other words, there is convincing evidence of an association between largest gift amount and total giving for all donors in this organization.

Calculate t-test statistic and p-value manually

\[ t = \frac{b_1-\beta}{SE_b} \]

# Test statistic

(3.910 -0) / .782
[1] 5
# P-value

2 * pt (q = 5, df = 50-2, lower.tail = FALSE)
[1] 8.061665e-06

For more on significance tests in regression, see LSR.

Final Note: Practical Significance

Just because a variable is statistically significant does not means it is practically significant!

  • Suppose the number of absences predicts grade in a course with the p-value = 0.0001 < \(\alpha = 0.05\), but the effect size is 0.1% lower grade in the course for each additional absence. More helpful would be knowing how well does the model fit, i.e., what’s \(r^2\)?

On your own

Collect two quantitative variables on the same individuals and calculate a confidence interval and perform a significance test for the slope.


Regression with Categorical Predictors

model_largest <- lm(totalGivingAmount ~ largestGiftAmount, data = sampleDonors)

summary(model_largest)

Call:
lm(formula = totalGivingAmount ~ largestGiftAmount, data = sampleDonors)

Residuals:
    Min      1Q  Median      3Q     Max 
-448.26  -62.28  -33.37   41.97  478.22 

Coefficients:
                  Estimate Std. Error t value   Pr(>|t|)    
(Intercept)         51.817     27.292   1.899     0.0636 .  
largestGiftAmount    3.910      0.782   5.000 0.00000807 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 138.8 on 48 degrees of freedom
Multiple R-squared:  0.3424,    Adjusted R-squared:  0.3287 
F-statistic:    25 on 1 and 48 DF,  p-value: 0.000008075

Model with Quantitative Variable

predict(model_largest, data.frame(largestGiftAmount = 50))
       1 
247.2973 

Use model to make prediction

predict(model, dataframe)

predict(model_largest, data.frame(largestGiftAmount = 50))
       1 
247.2973 

Model with Categorical Predictor

model_gender <- lm(totalGivingAmount ~ gender, data = sampleDonors)

table(sampleDonors$gender)

female   male 
    27     22 
summary(model_gender)

Call:
lm(formula = totalGivingAmount ~ gender, data = sampleDonors)

Residuals:
    Min      1Q  Median      3Q     Max 
-146.25 -106.25  -56.17   23.83  759.75 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)   139.17      33.05   4.211 0.000114 ***
gendermale     22.08      49.32   0.448 0.656465    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 171.7 on 47 degrees of freedom
  (1 observation deleted due to missingness)
Multiple R-squared:  0.004245,  Adjusted R-squared:  -0.01694 
F-statistic: 0.2004 on 1 and 47 DF,  p-value: 0.6565

\[ \hat{y} = 139.17 + 22.08(male) \]

0 = not male and 1 = yes male

Use model to make prediction

predict(model_gender, data.frame(gender = "male"))
       1 
161.2455 
139.17 + 22.08*1
[1] 161.25
predict(model_gender, data.frame(gender = c("male", "female")))
       1        2 
161.2455 139.1667 

Notice 139.17 + 22.08(0) = 139.17, the y-intercept


Interpreting Coefficients with Categorical Predictor

If a donor in this sample is male, we expect $22.08 more than if the donor is a female.

Note, however, that including gender is not statistically significant (P-value = 0.656465), which means the difference we observe between male and female donors may be due to chance, and not an actual inherent difference in giving.

A Categorical Variable with Multiple Levels

table(sampleDonors$urbanicity) 

  city  rural suburb   town  urban 
     9     14      7      8     11 
model_urbanicity <- lm(totalGivingAmount ~ urbanicity, data = sampleDonors)
model_urbanicity

Call:
lm(formula = totalGivingAmount ~ urbanicity, data = sampleDonors)

Coefficients:
     (Intercept)   urbanicityrural  urbanicitysuburb    urbanicitytown   urbanicityurban  
          199.28           -113.63             18.72            -84.47            -60.47  

\[ \hat{y} = 199.28 - 113.63(rural) + 18.72(suburb) - 84.47(town) - 60.47(urban) \]

a. Predict the total giving for a donor in this sample who lives in a rural area.

199.28 - 113.63*(1)

b. Predict the total giving for a donor in this sample who lives in the suburbs.

199.28 + 0 + 0
[1] 199.28

c. Predict the total giving for a donor in this sample who lives in a city (wait…).

199.28 - 113.63*(1)
[1] 85.65

City as “reference group”


Multiple Regression

Consider the data below from a recent year on six-year graduation rate (%), instructional expenditure per full-time student (in dollars), and median SAT score for 9 primarily undergraduate public universities and colleges in the western United States with enrollments between 10,000 and 20,000 (from Peck, et al., Stats: Learning from Data, 2nd ed., 234)

rate <- c(75, 71.5, 59.3, 56.4, 52.4, 48, 45.8, 42.7, 41.1)
expend <- c(6960, 7274, 5361, 5374, 5070, 5226, 5927, 5600, 5073)
SAT <- c(1242, 1114, 1014, 1070, 920, 888, 970, 937, 871)

colleges <- data.frame(rate, expend, SAT)
colleges

Compare two models

# Create two models, predicting graduation rate with different predictors
model_expend <- lm(rate ~ expend)
model_SAT <- lm(rate ~ SAT)

# Output the summary of each model
summary(model_expend)

Call:
lm(formula = rate ~ expend)

Residuals:
     Min       1Q   Median       3Q      Max 
-10.8078  -5.5289  -0.4167   6.2539   9.3058 

Coefficients:
              Estimate Std. Error t value Pr(>|t|)  
(Intercept) -12.648243  20.183306  -0.627    0.551  
expend        0.011685   0.003472   3.366    0.012 *
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 8.012 on 7 degrees of freedom
Multiple R-squared:  0.6181,    Adjusted R-squared:  0.5635 
F-statistic: 11.33 on 1 and 7 DF,  p-value: 0.01199
summary(model_SAT)

Call:
lm(formula = rate ~ SAT)

Residuals:
   Min     1Q Median     3Q    Max 
-5.952 -4.438 -1.505  3.838  6.631 

Coefficients:
             Estimate Std. Error t value Pr(>|t|)    
(Intercept) -37.20052   15.54075  -2.394  0.04790 *  
SAT           0.09162    0.01540   5.951  0.00057 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 5.267 on 7 degrees of freedom
Multiple R-squared:  0.835, Adjusted R-squared:  0.8114 
F-statistic: 35.41 on 1 and 7 DF,  p-value: 0.0005695
# Print r-squared
summary(model_expend)$r.squared
[1] 0.6180944
summary(model_SAT)$r.squared
[1] 0.8349573
# Print the p-value for the slope of each model, given it's in the 2nd row and 4 column
summary(model_expend)$coefficients[2, 4]
[1] 0.01198762
summary(model_SAT)$coefficients[2, 4]
[1] 0.0005695496

Choice:


Linear Regression Model with One Predictor

\[ \widehat{y} = b_0 + b_1 x_1 \]

Multiple Regression Model with p predictors:

\[ \widehat{y} = b_0 + b_1 x_1 + b_2 x_2 + \cdots + b_p x_p \]

To assess a multiple regression model, we use adjusted \(r^2\), which penalizes a model that uses too many useless predictors.

In other words, adjusted \(r^2\) will only increase if the new variables improve the model performance more than you’d expect by chance. For more info, see LSR here.


Perform multiple regression

lm(y ~ x1)

lm(y ~ x1 + x2 + x3 + …)

# Create multiple regression
model_mult <- lm(rate ~ expend + SAT)
summary(model_mult)

Call:
lm(formula = rate ~ expend + SAT)

Residuals:
   Min     1Q Median     3Q    Max 
-6.567 -2.919 -1.379  4.523  5.789 

Coefficients:
              Estimate Std. Error t value Pr(>|t|)  
(Intercept) -37.777476  16.503961  -2.289   0.0620 .
expend        0.002011   0.004115   0.489   0.6425  
SAT           0.080646   0.027766   2.905   0.0272 *
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 5.579 on 6 degrees of freedom
Multiple R-squared:  0.8413,    Adjusted R-squared:  0.7884 
F-statistic:  15.9 on 2 and 6 DF,  p-value: 0.003999
# Create multiple regression
model_mult <- lm(rate ~ expend + SAT)
summary(model_mult)

Call:
lm(formula = rate ~ expend + SAT)

Residuals:
   Min     1Q Median     3Q    Max 
-6.567 -2.919 -1.379  4.523  5.789 

Coefficients:
              Estimate Std. Error t value Pr(>|t|)  
(Intercept) -37.777476  16.503961  -2.289   0.0620 .
expend        0.002011   0.004115   0.489   0.6425  
SAT           0.080646   0.027766   2.905   0.0272 *
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 5.579 on 6 degrees of freedom
Multiple R-squared:  0.8413,    Adjusted R-squared:  0.7884 
F-statistic:  15.9 on 2 and 6 DF,  p-value: 0.003999

Comments

  1. R-squared increased slightly to 0.8413 (up from 0.835)

  2. Adjusted R-squared decreased to 0.7884 (down from 0.8114)

  3. The p-value is higher now for SAT.

  4. The p-value is not significant for expenditure.


Interpreting a coefficient in multiple regression

SAT => 0.081 => 0.081%

  • For each additional 1-point increase on median SAT score, we predict the graduation rate to increase 0.081%, holding expenditure constant.

Using more realistic units:

  • For each additional 100-point increase on median SAT score, we predict the graduation rate to increase 8.1%, holding expenditure constant.

On your own

Create your own multiple regression model (or models!) for data that interests you. Put it in your portfolio!

This material is for enrolled students’ academic use only and protected under U.S. Copyright Laws. This content must not be shared outside the confines of this course, in line with Eastern University’s academic integrity policies. Unauthorized reproduction, distribution, or transmission of this material, including but not limited to posting on third-party platforms like GitHub, is strictly prohibited and may lead to disciplinary action. You may not alter or remove any copyright or other notice from copies of any content taken from BrightSpace or Eastern University’s website. © Copyright Notice 2024, Eastern University - All Rights Reserved

LS0tCnRpdGxlOiAiNTUwIFJlZ3Jlc3Npb24gTm90ZXMiCm91dHB1dDogaHRtbF9ub3RlYm9vawplZGl0b3Jfb3B0aW9uczogCiAgbWFya2Rvd246IAogICAgd3JhcDogNzIKLS0tCgpgYGB7cn0KIyBEaXNwbGF5IG91dHB1dCB3aXRoaW4gdGhlIG5vdGVib29rCiMga25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFLCByZXN1bHRzID0gJ3Nob3cnKQpgYGAKCiMgSW50cm8gdG8gUmVncmVzc2lvbgoKRm9yIG1vcmUgb24gcmVncmVzc2lvbiwgW3NlZQpMU1JdKGh0dHBzOi8vbGVhcm5pbmdzdGF0aXN0aWNzd2l0aHIuY29tL2Jvb2svcmVncmVzc2lvbi5odG1sKS4KClVzaW5nIFggdG8gcHJlZGljdCBZCgpJcyB0aGVyZSBhbiBhc3NvY2lhdGlvbiBiZXR3ZWVuIFggYW5kIFk/CgpBICoqc2NhdHRlcnBsb3QqKiB2aXN1YWxpemVzIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB0d28gcXVhbnRpdGF0aXZlCnZhcmlhYmxlcyBvbiB0aGUgc2FtZSBpbmRpdmlkdWFscy4KCmBgYHtyfQphZ2UgPC0gYygxLCAyLCAyLCAyLCAyLCAzLCA0LCA0LCA1LCA1KQpjb3N0IDwtIGMoMzUwLCAzNzAsIDQ4MCwgNTIwLCA1OTAsIDU1MCwgNzUwLCA4MDAsIDc5MCwgOTUwKQoKcGxvdChhZ2UsIGNvc3QsIHBjaCA9IDE2LCBsYXMgPSAxLCBjZXggPSAxLCB4bGFiID0gIkFnZSBvZiBCdXMgKHllYXJzKSIsIHlsYWIgPSAiQW5udWFsIE1haW50ZW5hbmNlIENvc3QgKCQpIikKCiNGcm9tIENhbW0sIGV0LiBhbC4sIEJ1c2luZXNzIEFuYWx5dGljcywgM2UsIDM1NgoKIyBBICp0aW1lIHNlcmllcyogaXMgYSBzY2F0dGVycGxvdCB3aXRoIHRpbWUgKHNlY29uZHMsIG1vbnRocywgeWVhcnMsIGRlY2FkZXMpIG9uIHRoZSB4LWF4aXMgKGFzIHRoZSBleHBsYW5hdG9yeSB2YXJpYWJsZSkuCmBgYAoKT3V0Y29tZSA9IE1vZGVsICsgRXJyb3IKCkF2b2lkICJvdmVyZml0dGluZyI7IGRvbid0IHBsYXkgY29ubmVjdCB0aGUgZG90cyEKCkludHJvZHVjdG9yeSwgYmFzZWxpbmUgTUwgbW9kZWxzCgp8IE1vZGVsIHwgRGF0YSB8CnwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfAp8IExpbmVhciByZWdyZXNzaW9uIHwgT25lIHByZWRpY3RvciB3aXRoIGEgY29udGludW91cyBvdXRjb21lIHwKfCBNdWx0aXBsZSByZWdyZXNzaW9uIHwgTXVsdGlwbGUgcHJlZGljdG9ycyB3aXRoIGEgY29udGludW91cyBvdXRjb21lIHwKfCBMb2dpc3RpYyByZWdyZXNzaW9uIHwgT25lIHByZWRpY3RvciB3aXRoIGEgYmluYXJ5IG91dGNvbWUgfAp8IE11bHRpcGxlIGxvZ2lzdGljIHJlZ3Jlc3Npb24gfCBNdWx0aXBsZSBwcmVkaWN0b3JzIHdpdGggYSBiaW5hcnkgb3V0Y29tZSB8CnwgUG9seW5vbWlhbCByZWdyZXNzaW9uIHwgUmVsYXRpb25zaGlwIGJldHdlZW4gdGhlIHByZWRpY3RvcihzKSBhbmQgdGhlIGNvbnRpbnVvdXMgb3V0Y29tZSBpcyBub25saW5lYXIgfAp8IC4uLmFuZCBtb3JlLi4uIHwgIHwKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKQSAqKnJlc3BvbnNlIHZhcmlhYmxlKiogKHkpIG1lYXN1cmVzIGFuIG91dGNvbWUgb2YgYSBzdHVkeS4KCi0gICBEZXBlbmRlbnQgdmFyaWFibGUsIG91dGNvbWUKCkFuICoqZXhwbGFuYXRvcnkgdmFyaWFibGUqKiAoeCkgbWF5IGhlbHAgcHJlZGljdCBvciBleHBsYWluIGNoYW5nZXMgaW4gYQpyZXNwb25zZSB2YXJpYWJsZS4KCi0gICBJbmRlcGVuZGVudCB2YXJpYWJsZSwgcHJlZGljdG9yLCBmZWF0dXJlCgpJZiBrbm93aW5nIHRoZSB2YWx1ZSBvZiBvbmUgdmFyaWFibGUgaGVscHMgdXMgcHJlZGljdCB0aGUgdmFsdWUgb2YgdGhlCm90aGVyLCB0aGVyZSBpcyBhbiAqKmFzc29jaWF0aW9uKiogYmV0d2VlbiB0aGUgdHdvIHZhcmlhYmxlcy4KCipSZWdyZXNzaW5nIFkgb24gWCoKCmBgYHtyfQojIG10Y2FycyBpcyBidWlsdC1pbiBkYXRhIHdpdGhpbiBSCgpwbG90KG10Y2FycyRocCwgbXRjYXJzJG1wZywgcGNoID0gMTYsIGxhcyA9IDEsIGNleCA9IDEsIG1haW4gPSAiVGVybWlub2xvZ3kgUHJhY3RpY2UiLCB4bGFiID0gIkhvcnNlcG93ZXIiLCB5bGFiID0gIk1pbGVzIFBlciBHYWxsb24iKQpgYGAKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyBZb3VyIFR1cm4KClRoZSBkYXRhc2V0IGNvbWVzIGZyb20gYSBuYXRpb25hbCB2ZXRlcmFucycgb3JnYW5pemF0aW9uIHRoYXQgZnJlcXVlbnRseQpzb2xpY2l0cyBkb25hdGlvbnMgdGhyb3VnaCBkaXJlY3QgbWFpbCBjYW1wYWlnbnMgdG8gaXRzIGRhdGFiYXNlIG9mCmN1cnJlbnQgYW5kIHByb3NwZWN0aXZlIGRvbm9ycy4gVGhlIG9yZ2FuaXphdGlvbiBzZW50IG91dCBhIHRlc3QgbWFpbGluZwp0byBhIGdyb3VwIG9mIHBvdGVudGlhbCBkb25vcnMgYW5kIGdhdGhlcmVkIGluZm9ybWF0aW9uIG9uIHRoZSByZXNwb25zZQp0byB0aGF0IHRlc3QgbWFpbGluZy4KCkZyb20gKlByYWN0aWNhbCBNYWNoaW5lIExlYXJuaW5nIHdpdGggUiogYnkgTndhbmdhbmdhIGFuZCBDaGFwcGxlIChwLgoxNjYtNjgpCgojIyBEb3dubG9hZCB0aGUgZGF0YQoKRG93bmxvYWQgdGhlIGBkb25vcnMuY3N2YCBhbmQgdGhpcyBSbWQgbm90ZWJvb2sgZnJvbSBCcmlnaHRzcGFjZS4KClJlbWVtYmVyIHRvIHNhdmUgdGhlIGNzdiBpbiB0aGUgc2FtZSBmb2xkZXIgYXMgdGhlIFJtZCBub3RlYm9vazsKb3RoZXJ3aXNlLCB5b3Ugd2lsbCBuZWVkIHRvIHNldCB5b3VyIHdvcmtpbmcgZGlyZWN0b3J5LiBTZWUgcHJldmlvdXMKbW9kdWxlIGZvciB2aWRlbyBhc3Npc3RhbmNlLgoKIyMgTG9hZCB0aGUgZGF0YQoKYGBge3J9CiMgTG9hZCB0aGUgZGF0YSBhcyAiZG9ub3JzIgoKZG9ub3JzIDwtIHJlYWQuY3N2KCJkb25vcnMgLSBDb3B5LmNzdiIpCgpgYGAKCiMjIEZhbWlsaWFyaXplIHlvdXJzZWxmIHdpdGggdGhlIGRhdGEKCmBgYHtyfQpzdW1tYXJ5KGRvbm9ycykKYGBgCgojIyBTdW1tYXJpemUgZGF0YQoKYGBge3J9CgoKIyBOb3RlIHNvbWUgd2VpcmQgZmVhdHVyZXMhCiMgU2VlIGFnZXMsIG51bWJlckNoaWxkcmVuLCBsb3RzIG9mIE5BcwojIFdlIG5lZWQgYSBjb2RlYm9vayEKYGBgCgojIFNhbXBsaW5nIGZyb20gdGhlIFBvcHVsYXRpb24KClNhbXBsZTogNTAgZG9ub3JzIChuID0gNTApCgpQb3B1bGF0aW9uOiBBbGwgZG9ub3JzIChOID0gOTUsNDEyKQoKYGBge3J9CiMgRm9yIHJlcHJvZHVjaWJpbGl0eQpzZXQuc2VlZCgxKQoKIyBSYW5kb21seSBzZWxlY3QgNTAgbnVtYmVycyB0byByZXByZXNlbnQgcm93cyBpbiBkYXRhZnJhbWUKc2FtcGxlSURzIDwtIHNhbXBsZSgxOjk1NDEyLCBzaXplID0gNTAsIHJlcGxhY2UgPSBGQUxTRSkKCiMgU2VsZWN0IHRoZSBjb2x1bW5zIEknbSBpbnRlcmVzdGVkIGluCm15Q29sdW1ucyA8LSBjKCJhZ2UiLCAiaW5jb21lUmF0aW5nIiwgInRvdGFsR2l2aW5nQW1vdW50IiwgImxhcmdlc3RHaWZ0QW1vdW50IiwgImlzSG9tZW93bmVyIiwgImdlbmRlciIsICJ1cmJhbmljaXR5IiwgInJlc3BvbmRlZE1haWxpbmciKQoKIyBTdG9yZSB0aGUgcmFuZG9tbHkgc2VsZWN0ZWQgcm93cyBvbiB0aGUgZ2l2ZW4gY29sdW1ucwpzYW1wbGVEb25vcnMgPC0gZG9ub3JzW3NhbXBsZUlEcywgbXlDb2x1bW5zXQpzYW1wbGVEb25vcnMKCmBgYAoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIyBSZXZpZXcgY29ycmVsYXRpb24gbWF0cml4CgpXaXRoIE5BcwoKYGBge3J9CmNvcihzYW1wbGVEb25vcnNbMTo1MCwgbmEucm09RkFMU0UsIGMoImFnZSIsICJpbmNvbWVSYXRpbmciLCAidG90YWxHaXZpbmdBbW91bnQiLCAibGFyZ2VzdEdpZnRBbW91bnQiKV0pCj9jb3IKYGBgCgpSZW1vdmluZyBOQXMKCmBgYHtyfQpyb3VuZChjb3Ioc2FtcGxlRG9ub3JzWzE6NTAsIGMoImFnZSIsICJpbmNvbWVSYXRpbmciLCAidG90YWxHaXZpbmdBbW91bnQiLCAibGFyZ2VzdEdpZnRBbW91bnQiKV0sIHVzZSA9ICJjb21wbGV0ZS5vYnMiKSwgMikKYGBgCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMjIEEgU2NhdHRlcnBsb3QKCmBgYHtyfQpwbG90KHNhbXBsZURvbm9ycyRhZ2UsIAogICAgIHNhbXBsZURvbm9ycyRpbmNvbWVSYXRpbmcsIAogICAgIGNleCA9IDAuNSwKICAgICBsYXMgPSAxLAogICAgIHBjaCA9IDE2LCAKICAgICB4bGFiID0gIkFnZSIsIAogICAgIHlsYWIgPSAiSW5jb21lIFJhdGluZyIpCmBgYAoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIC0tLQoKIyBUaGUgTGluZSBvZiBCZXN0IEZpdAoKXCpCZSBzdXJlIHlvdSBmaW5pc2hlZCB0aGUgdGFzayBpbiB0aGUgcHJpb3IgdmlkZW8hCgpvdXRjb21lID0gbW9kZWwgKyBlcnJvcgoKYGBge3J9CmdpZnRDb3IgPC0gcm91bmQoY29yKHNhbXBsZURvbm9ycyRsYXJnZXN0R2lmdEFtb3VudCwgc2FtcGxlRG9ub3JzJHRvdGFsR2l2aW5nQW1vdW50KSwgMikKCnBsb3Qoc2FtcGxlRG9ub3JzJGxhcmdlc3RHaWZ0QW1vdW50LCAKICAgICBzYW1wbGVEb25vcnMkdG90YWxHaXZpbmdBbW91bnQsIAogICAgIGNleCA9IDEuNSwKICAgICBsYXMgPSAxLAogICAgIHBjaCA9IDE2LCAKICAgICB4bGFiID0gIkxhcmdlc3QgR2lmdCBBbW91bnQiLCAKICAgICB5bGFiID0gIlRvdGFsIEdpdmluZyIsCiAgICAgbWFpbiA9IHBhc3RlKCJyID0gIiwgZ2lmdENvcikpCmBgYAoKUmV2aWV3OiBEZXNjcmliZSB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gdGhlIGxhcmdlc3QgZ2lmdCBhbW91bnQgYW5kCnRvdGFsIGdpdmluZyBmb3IgdGhpcyBzYW1wbGUgb2YgNTAgZG9ub3JzLgoKIyMgV2hhdCBsaW5lIGJlc3QgbW9kZWxzIHRoaXMgcmVsYXRpb25zaGlwPwoKLSAgICJUcmVuZCBsaW5lIgoKLSAgICJMaW5lIG9mIEJlc3QgRml0IgoKLSAgICJMZWFzdCBTcXVhcmVzIFJlZ3Jlc3Npb24gTGluZSIgKExTUkwpCgotICAgIk9yZGluYXJ5IExlYXN0IFNxdWFyZXMgUmVncmVzc2lvbiIgKE9MU1IpCgpgbG0oeSB+IHgpYCA9IGxpbmVhciBtb2RlbCBwcmVkaWN0aW5nIHkgZ2l2ZW4geAoKYGBge3J9CiMgQ2FsY3VsYXRlIGNvZWZmaWNpZW50cyBvZiBiZXN0IGZpdHRpbmcgbGluZWFyIG1vZGVsCgptb2RlbCA8LSBsbShzYW1wbGVEb25vcnMkdG90YWxHaXZpbmdBbW91bnQgfiBzYW1wbGVEb25vcnMkbGFyZ2VzdEdpZnRBbW91bnQpCiMjcmVzcG9uc2UgZmlyc3QgKFkpLCBwcmVkaWN0b3IgKHgpIAoKIyM1MS44MiBpcyB0aGUgY29lZmYsIHkgaW50ZXJjZXB0ICwgdG91Y2hlcyB0aGUgeSBheGlzCgoKIyMzLjkxIGlzIHRoZSBzbG9wZSBvZiB0aGUgbGluZQoKbW9kZWwKYGBgCgoqKkludGVyY2VwdCoqIGlzIHRoZSB5LWludGVyY2VwdCwgd2hlcmUgdGhlIGJlc3QtZml0dGluZyBsaW5lIGhpdHMgdGhlCnktYXhpcwoKKipTbG9wZSoqIGlzIHRoZSAiaW5jbGluZSIgb2YgdGhlIGJlc3QtZml0dGluZyBsaW5lCgoiU2xvcGUtSW50ZXJjZXB0IEZvcm0iCgokJAp5ID0gbXggKyBiCiQkCgpgbWAgaXMgdGhlIHNsb3BlIGBiYCBpcyB0aGUgeS1pbnRlcmNlcHQKCkluIHN0YXRpc3RpY3MsIHdlIHdyaXRlIGl0IGRpZmZlcmVudGx5OiAkJApcd2lkZWhhdHt5fSA9IGEgKyBiKHgpIFxcClx3aWRlaGF0e3l9ID0gYSArIGJ4IFxcClx3aWRlaGF0e3l9ID0gYl8wICsgYl8xeCAKJCQKClRoZSBsYXR0ZXIgZm9ybSBtYWtlcyB0aGUgbW9zdCBzZW5zZSwgZ2l2ZW4gbXVsdGlwbGUgcmVncmVzc2lvbiAod2hlbiB3ZQpoYXZlIG11bHRpcGxlIGNvZWZmaWNpZW50cywgbm90IGp1c3QgdHdvKS4KCiRiXzAkIGFuZCAkYl8xJCBhcmUgcGFyYW1ldGVycyAoYWxzbyBhcyAkXHRoZXRhXzAkIGFuZCAkXHRoZXRhXzEkIG9yCiRcYmV0YV8wJCBhbmQgJFxiZXRhXzEkKQoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgpXaHkgdGhlIGhhdCAkXGhhdHt5fSQ/IFdoeSBpcyBpdCBpbXBvcnRhbnQ/CgpPdXRjb21lID0gTW9kZWwgKyBFcnJvcgoKJCR5ID0gYSArIGJ4ICsgXGVwc2lsb24gXFwkJCBvciAkJFxoYXR7eX0gPSBhICsgYnggXFwkJAoKSXQncyBhIHRyZW5kLCBub3QgYSBndWFyYW50ZWU7IG5vdCAiZGV0ZXJtaW5pc3RpYyIsIG5vdCAiZXhhY3QiCgpgbG0oeSB+IHgpJGNvZWZmaWNpZW50c1sxXWAgdG8gYWNjZXNzIHRoZSB5LWludGVyY2VwdAoKYGxtKHkgfiB4KSRjb2VmZmljaWVudHNbMl1gIHRvIGFjY2VzcyB0aGUgc2xvcGUKCmBgYHtyfQojIFN0b3JlIHRoZSBjb2VmZmljaWVudHMgb24gdGhlIGxpbmVhciBtb2RlbApteV9pbnRlcmNlcHQgPC0gcm91bmQobW9kZWwkY29lZmZpY2llbnRzWzFdLCAxKQpteV9zbG9wZSA8LSByb3VuZChtb2RlbCRjb2VmZmljaWVudHNbMl0sIDEpCgojIFBsb3Qgc2NhdHRlcnBsb3QKcGxvdChzYW1wbGVEb25vcnMkbGFyZ2VzdEdpZnRBbW91bnQsIAogICAgIHNhbXBsZURvbm9ycyR0b3RhbEdpdmluZ0Ftb3VudCwgCiAgICAgY2V4ID0gMS41LAogICAgIGxhcyA9IDEsCiAgICAgcGNoID0gMTYsIAogICAgIHhsYWIgPSAiTGFyZ2VzdCBHaWZ0IEFtb3VudCIsIAogICAgIHlsYWIgPSAiVG90YWwgR2l2aW5nIiwKICAgICBtYWluID0gcGFzdGUoIkxTUkw6ICIsICJQcmVkaWN0ZWQgeSA9ICIsIG15X2ludGVyY2VwdCwgIisiLCBteV9zbG9wZSwgIih4KSIpKQoKIyBQbG90IExTUkwKYWJsaW5lKG1vZGVsLCAKICAgICAgIGx3ZCA9IDIsICAgICAgICAjIG1ha2UgdGhlIGxpbmUgd2lkZXIKICAgICAgIGNvbCA9ICJyZWQiICAgICAjIGNvbG9yIHRoZSBsaW5lCiAgICAgICApCmBgYAoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIC0tLQoKIyBSZXNpZHVhbHMKCkJlZm9yZSB3ZSBjYW4gdW5kZXJzdGFuZCB3aGF0IG1ha2VzIHRoZSBMU1JMIHRoZSAiYmVzdCBmaXR0aW5nIiBsaW5lLCB3ZQpuZWVkIHRvIHVuZGVyc3RhbmQgd2hhdCBhIHJlc2lkdWFsIGlzLgoKTGV0J3MgY29uc2lkZXIgdGhlIHNjYXR0ZXJwbG90OgoKYGBge3J9CiMgU3RvcmUgdGhlIGNvZWZmaWNpZW50cyBvbiB0aGUgbGluZWFyIG1vZGVsCm15X2ludGVyY2VwdCA8LSByb3VuZChtb2RlbCRjb2VmZmljaWVudHNbMV0sIDEpCm15X3Nsb3BlIDwtIHJvdW5kKG1vZGVsJGNvZWZmaWNpZW50c1syXSwgMSkKCiMgUGxvdCBzY2F0dGVycGxvdApwbG90KHNhbXBsZURvbm9ycyRsYXJnZXN0R2lmdEFtb3VudCwgCiAgICAgc2FtcGxlRG9ub3JzJHRvdGFsR2l2aW5nQW1vdW50LCAKICAgICBjZXggPSAxLjUsCiAgICAgbGFzID0gMSwKICAgICBwY2ggPSAxNiwgCiAgICAgeGxhYiA9ICJMYXJnZXN0IEdpZnQgQW1vdW50IiwgCiAgICAgeWxhYiA9ICJUb3RhbCBHaXZpbmciLAogICAgIG1haW4gPSBwYXN0ZSgiTFNSTDogIiwgIlByZWRpY3RlZCB5ID0gIiwgbXlfaW50ZXJjZXB0LCAiKyIsIG15X3Nsb3BlLCAiKHgpIikpCgojIFBsb3QgTFNSTAphYmxpbmUobW9kZWwsIAogICAgICAgbHdkID0gMiwgICAgICAgICMgbWFrZSB0aGUgbGluZSB3aWRlcgogICAgICAgY29sID0gInJlZCIgICAgICMgY29sb3IgdGhlIGxpbmUKICAgICAgICkKYGBgCgotICAgQmFzZWQgb24gdGhlIExTUkwsIHdoaWNoIGRvbm9ycyBnYXZlICptb3JlIHRoYW4gZXhwZWN0ZWQqIGJhc2VkIG9uCiAgICB0aGVpciBsYXJnZXN0IGdpZnQgYW1vdW50PwoKLSAgIEJhc2VkIG9uIHRoZSBMU1JMLCB3aGljaCBkb25vcnMgZ2F2ZSAqbGVzcyB0aGFuIGV4cGVjdGVkKiBiYXNlZCBvbgogICAgdGhlaXIgbGFyZ2VzdCBnaWZ0IGFtb3VudD8KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKQSAqcmVzaWR1YWwqIGlzIHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gdGhlIGFjdHVhbCB2YWx1ZSBvZiB5IGFuZCB0aGUKdmFsdWUgb2YgeSBwcmVkaWN0ZWQgYnkgdGhlIHJlZ3Jlc3Npb24gbGluZS4KClJlc2lkdWFsID0gYWN0dWFsIHkgLSBwcmVkaWN0ZWQgeQoKb3IKClJlc2lkdWFsID0gb2JzZXJ2ZWQgeSAtIGV4cGVjdGVkIHkKCm9yCgokJApcZXBzaWxvbiA9IHkgLSBcaGF0e3l9CiQkCgpBICpwb3NpdGl2ZSogcmVzaWR1YWwgbWVhbnMuLi4KCkEgKm5lZ2F0aXZlKiByZXNpZHVhbCBtZWFucy4uLgoKYGBge3J9CiMgQ3JlYXRlIGEgc2ltcGxlIGRhdGFzZXQKeCA8LSBjKDEsIDIsIDMsIDQsIDUpCnkgPC0gYygyLCA0LCA2LCA4LCAxMCkgICMgUGVyZmVjdGx5IGxpbmVhciByZWxhdGlvbnNoaXAgKHkgPSAyeCkKCiMgSW50cm9kdWNlIG9uZSBwb3NpdGl2ZSByZXNpZHVhbCBhbmQgb25lIG5lZ2F0aXZlIHJlc2lkdWFsCnlbMl0gPC0geVsyXSArIDIgICMgUG9zaXRpdmUgcmVzaWR1YWwKeVs0XSA8LSB5WzRdIC0gMiAgIyBOZWdhdGl2ZSByZXNpZHVhbAoKIyBGaXQgYSBsaW5lYXIgbW9kZWwKZXhfbW9kZWwgPC0gbG0oeSB+IHgpCgojIFBsb3QgdGhlIGRhdGEgYW5kIHRoZSBmaXR0ZWQgbGluZQpwbG90KHgsIHksIHBjaCA9IDE2LCBjZXggPSAxLjUsIGxhcyA9IDEsIGNvbCA9ICJibGFjayIsIG1haW4gPSAiUG9zaXRpdmUgJiBOZWdhdGl2ZSBSZXNpZHVhbHMiKQphYmxpbmUoZXhfbW9kZWwsIGNvbCA9ICJibHVlIiwgbHdkID0gMykKCiMgQWRkIHJlc2lkdWFsIGxpbmVzCmZvciAoaSBpbiAxOmxlbmd0aCh4KSkgewogIGxpbmVzKGMoeFtpXSwgeFtpXSksIGMoeVtpXSwgcHJlZGljdChleF9tb2RlbClbaV0pLCBjb2wgPSAicmVkIiwgbHR5ID0gMiwgbHdkID0gMi41KQp9CmBgYAoKInRoZSBsaW5lICp1bmRlcnByZWRpY3RzKiB3aGVuLi4uIgoKInRoZSBsaW5lICpvdmVycHJlZGljdHMqIHdoZW4uLi4iCgpJZiBhIHJlc2lkdWFsIGlzIGNsb3NlIHRvIChvciBlcXVhbHMpIHplcm8sIHRoYXQgbWVhbnMuLi4KClRoZSBzdW0gb2YgYWxsIHRoZSByZXNpZHVhbHMgPSBcX1xfLgoKTm90ZTogQSByZXNpZHVhbCBpcyB0aGUgKnZlcnRpY2FsKiBkaXN0YW5jZSBmcm9tIGEgcG9pbnQgdG8gdGhlIExTUkwKCmBgYHtyfQojIEZpbmQgdGhlIGRvbm9yIHdobyBnYXZlICQxNTAgYXMgbGFyZ2VzdCBnaWZ0IGFtb3VudApzYW1wbGVEb25vcnNbc2FtcGxlRG9ub3JzJGxhcmdlc3RHaWZ0QW1vdW50ID09IDE1MCwgXQoKIyBBbHRlcm5hdGUgYXBwcm9hY2gKd2hpY2goc2FtcGxlRG9ub3JzJGxhcmdlc3RHaWZ0QW1vdW50ID09IDE1MCkKIyMjIDQ2IGlzIHRoZSA0NnRoIHJvdyBpbiBzYW1wbGVEb25vcnMgYW5kIGRvZXMgbm90IG1hdGNoIHRoZSBvcmlnaW5hbCBpbmRleCBvZiAxMzYwMgpgYGAKCmBgYHtyfQojIFBpY2sgc2VsZWN0IGNvbHVtbnMKc2FtcGxlRG9ub3JzW3NhbXBsZURvbm9ycyRsYXJnZXN0R2lmdEFtb3VudCA9PSAxNTAsIGMoImxhcmdlc3RHaWZ0QW1vdW50IiwgInRvdGFsR2l2aW5nQW1vdW50IildCmBgYAoKIyMgQ2FsY3VsYXRlIGFuZCBpbnRlcnByZXQgdGhlIHJlc2lkdWFsIGZvciB0aGlzIGRvbm9yLgoKYGBge3J9CjE5MCAtICg1MS44ICsgMy45KiAoMTUwKSkKCiMjIApgYGAKCkludGVycHJldGF0aW9uOiBmb3IgdGhlIGlzIGRvbmVyIHdob3MgbGFyZ2V0c3QgZ2l0ZiB3YXMgMTUwLCB0aGUgbHNybApvdmVycHJlZCB0aGVpciB0b3RhbCBnaXZpbmcgbXkgNDQ2CgpvciB0aGlzIGRvbm9yIGdhdmUgNDQ2IGxlc3MgdGhhbiBwcmVkaWN0ZWQuCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMjIFByYWN0aWNlCgpGaW5kIHRoZSBkb25vciBpbiB0aGlzIHNhbXBsZSB3aG9zZSBsYXJnZXN0IGdpZnQgYW1vdW50IHdhcyBcJDEwMCwgYW5kCnRoZW4gY2FsY3VsYXRlICphbmQqIGludGVycHJldCB0aGUgcmVzaWR1YWwgZm9yIHRoaXMgZG9ub3IuCgpgYGB7cn0Kd2hpY2goc2FtcGxlRG9ub3JzJGxhcmdlc3RHaWZ0QW1vdW50PT0xMDApCnNhbXBsZURvbm9yc1tzYW1wbGVEb25vcnMkbGFyZ2VzdEdpZnRBbW91bnQgPT0gMTAwLCBjKCJsYXJnZXN0R2lmdEFtb3VudCIsICJ0b3RhbEdpdmluZ0Ftb3VudCIpXQojd2hpY2goc2FtcGxlRG9ub3JzJGxhcmdlc3RHaWZ0QW1vdW50ID09IDE1MCkKIzE5MCAtICg1MS44ICsgMy45KiAoMTUwKSkKCjkyMS0gKDUxLjggKyAzLjkoMTAwKSkKCmBgYAoKSW50ZXJwcmV0YXRpb246CgojIyBSIHN0b3JlcyB0aGUgcmVzaWR1YWxzCgpgbG0oeSB+IHgpJHJlc2lkdWFsc2AKCmBgYHtyfQojIExpc3QgYWxsIHJlc2lkdWFscwptb2RlbCRyZXNpZHVhbHMKCiMgT3JkZXIgdGhlIHJlc2lkdWFscwojc29ydChtb2RlbCRyZXNpZHVhbHMpCmBgYAoKIyMjIElsbHVzdHJhdGluZyByZXNpZHVhbHMKCmBgYHtyfQojIENyZWF0ZSBsaW5lYXIgcmVncmVzc2lvbiBtb2RlbAptb2RlbCA8LSBsbShzYW1wbGVEb25vcnMkdG90YWxHaXZpbmdBbW91bnQgfiBzYW1wbGVEb25vcnMkbGFyZ2VzdEdpZnRBbW91bnQpCgojIFBsb3Qgc2NhdHRlcnBsb3QKcGxvdChzYW1wbGVEb25vcnMkbGFyZ2VzdEdpZnRBbW91bnQsIAogICAgIHNhbXBsZURvbm9ycyR0b3RhbEdpdmluZ0Ftb3VudCwgCiAgICAgY2V4ID0gMS41LAogICAgIGxhcyA9IDEsCiAgICAgcGNoID0gMTYsIAogICAgIHhsYWIgPSAiTGFyZ2VzdCBHaWZ0IEFtb3VudCIsIAogICAgIHlsYWIgPSAiVG90YWwgR2l2aW5nIiwKICAgICBtYWluID0gcGFzdGUoIkxTUkw6ICIsICJ5LWhhdCA9ICIsIG15X2ludGVyY2VwdCwgIisiLCBteV9zbG9wZSwgIih4KSIpKQoKIyBQbG90IExTUkwKYWJsaW5lKG1vZGVsLCAKICAgICAgIGx3ZCA9IDIsICAgICAgICAjIG1ha2UgdGhlIGxpbmUgd2lkZXIKICAgICAgIGNvbCA9ICJyZWQiICAgICAjIGNvbG9yIHRoZSBsaW5lCiAgICAgICApCgojIFBsb3QgZWFjaCByZXNpZHVhbCBmcm9tIHkgdG8gcHJlZGljdGVkIHkgKGkuZS4gZml0dGVkIHZhbHVlKSB3aXRoIHNlZ21lbnRzKCkKIyMgc2VnbWVudHMoeDEsIHkxLCB4MiwgeTIpCgpzZWdtZW50cyhzYW1wbGVEb25vcnMkbGFyZ2VzdEdpZnRBbW91bnQsICMgeDEKICAgICAgICAgZml0dGVkKG1vZGVsKSwgICAgICAgICAgICAgICAgICAgIyB5MQogICAgICAgICBzYW1wbGVEb25vcnMkbGFyZ2VzdEdpZnRBbW91bnQsICMgeDIKICAgICAgICAgc2FtcGxlRG9ub3JzJHRvdGFsR2l2aW5nQW1vdW50LCAgIyB5MgogICAgICAgICBjb2wgPSAiYmx1ZSIsCiAgICAgICAgIGx0eSA9IDIpIApgYGAKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyMgUmVzaWR1YWwgUGxvdAoKV2UgY2FuIHBsb3QgdGhlIGV4cGxhbmF0b3J5IHZhcmlhYmxlIChvciB0aGUgcHJlZGljdGlvbnMpIHZzLiB0aGUKcmVzaWR1YWxzIG9uIGEgcGxvdCBjYWxsZWQgYSAqcmVzaWR1YWwgcGxvdCouIE1vcmUgb24gcmVzaWR1YWwgcGxvdHMKbGF0ZXIuCgpgYGB7cn0KIyBQbG90IHNjYXR0ZXJwbG90IHdpdGggTFNSTApwbG90KHNhbXBsZURvbm9ycyRsYXJnZXN0R2lmdEFtb3VudCwgCiAgICAgc2FtcGxlRG9ub3JzJHRvdGFsR2l2aW5nQW1vdW50LCAKICAgICBjZXggPSAxLjUsCiAgICAgbGFzID0gMSwKICAgICBwY2ggPSAxNiwgCiAgICAgeGxhYiA9ICJMYXJnZXN0IEdpZnQgQW1vdW50IiwgCiAgICAgeWxhYiA9ICJUb3RhbCBHaXZpbmciLAogICAgIG1haW4gPSAiU2NhdHRlcnBsb3QiKQphYmxpbmUobW9kZWwsIAogICAgICAgbHdkID0gMiwgICAgICAgICMgbWFrZSB0aGUgbGluZSB3aWRlcgogICAgICAgY29sID0gInJlZCIgICAgICMgY29sb3IgdGhlIGxpbmUKICAgICAgICkKCiMgUmVzaWR1YWwgUGxvdApwbG90KHNhbXBsZURvbm9ycyRsYXJnZXN0R2lmdEFtb3VudCwgCiAgICAgbW9kZWwkcmVzaWR1YWxzLCAKICAgICB4bGFiID0gIkxhcmdlc3QgR2lmdCBBbW91bnQiLCAKICAgICB5bGFiID0gIlJlc2lkdWFsIiwgCiAgICAgbGFzID0gMSwKICAgICBwY2ggPSAxNiwKICAgICBjZXggPSAxLjUsCiAgICAgbWFpbiA9ICJSZXNpZHVhbCBQbG90IikKCmFibGluZShoID0gMCwgY29sID0gInJlZCIsIGx3ZCA9IDIpCmBgYAoKUiBjb21lcyB3aXRoIGEgYnVpbHQtaW4gcmVzaWR1YWwgcGxvdCAod2hpY2ggaW5jbHVkZXMgb3RoZXIgZGlhZ25vc3RpYwpncmFwaHMpLgoKYGBge3J9CnBsb3QobW9kZWwpCmBgYAoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIC0tLQoKIyBXaHkgaXMgdGhlIGxlYXN0LXNxdWFyZXMgcmVncmVzc2lvbiBsaW5lIHRoZSAiYmVzdCBmaXR0aW5nIiBsaW5lPwoKW1NxdWFyZWQgUmVzaWR1YWxzXShodHRwczovL3d3dy5nZW9nZWJyYS5vcmcvbS9EdHNNa2oyUikKCkFub3RoZXIgYXBwbGV0OiBbR3Vlc3MgdGhlIExTUkwKQXBwbGV0XShodHRwczovL3BoZXQuY29sb3JhZG8uZWR1L3NpbXMvaHRtbC9sZWFzdC1zcXVhcmVzLXJlZ3Jlc3Npb24vbGF0ZXN0L2xlYXN0LXNxdWFyZXMtcmVncmVzc2lvbl9lbi5odG1sKQoKVGhlIGxlYXN0LXNxdWFyZXMgcmVncmVzc2lvbiBpcyB0aGUgYmVzdC1maXR0aW5nIGxpbmUgYmVjYXVzZSBpdCBcX1xfXF8KdGhlIFxfXF9cXyBvZiB0aGUgc3F1YXJlZCBcX1xfXF8uCgokJApkXzFeMitkXzJeMitkXzNeMitkXzReMisgLi4uK2Rfbl4yPSBcbWJveHthIG1pbmltdW19CiQkCgokJAplXzFeMitlXzJeMitlXzNeMitlXzReMisgLi4uK2Vfbl4yPSBcbWJveHthIG1pbmltdW19CiQkCgokJApcdW5kZXJzZXR7Yl8wLCBiXzF9e1x0ZXh0e21pbn19IFxtYm94eyB9XFNpZ21hKHJlc2lkdWFscyleMgokJAoKJCQKXHVuZGVyc2V0e2JfMCwgYl8xfXtcdGV4dHttaW59fSBcbWJveHsgfVxTaWdtYSh5LWJfMC1iXzF4KV4yCiQkCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMjIEhvdyBhcmUgdGhlIGNvZWZmaWNpZW50cyBvZiB0aGUgTFNSTCBjYWxjdWxhdGVkPwoKIyMjIFRoZSBzbG9wZQoKMS4gIFRoZSBzbG9wZSB1c2luZyBhdmVyYWdlczoKCiQkCmIgPSBcZnJhY3tcc3VtX3tpPTF9Xm4gKHhfaSAtIFxiYXJ7eH0pKHlfaSAtIFxiYXJ7eX0pfXtcc3VtX3tpPTF9Xm4gKHhfaSAtIFxiYXJ7eH0pXjJ9CiQkCgoyLiAgVGhlIHNsb3BlIHVzaW5nIHN1bXMgb25seTogJCQKICAgIGIgPSBcZGlzcGxheXN0eWxlXGZyYWN7XGRpc3BsYXlzdHlsZVwgbiBcc3VtIHhfaSB5X2kgLSBcc3VtIHhfaSBcc3VtIHlfaX17XGRpc3BsYXlzdHlsZVwgbiBcc3VtIHhfaV4yIC0gKFxzdW0geF9pKV4yfQogICAgJCQKClRvIGRlcml2ZSB0aGVzZSBmb3JtdWxhcywgd2UgbmVlZCBtdWx0aXZhcmlhdGUgY2FsY3VsdXMgYW5kIGxpbmVhcgphbGdlYnJhLgoKMy4gIFRoZSBzbG9wZSB1c2luZyBjb3JyZWxhdGlvbiAkJCBiID0gclxmcmFje3NfeX17c194fSQkCgojIyMgVGhlIHktaW50ZXJjZXB0CgokXGhhdHt5fSA9IGEgKyBiKHgpJAoKVGhlIGNlbnRyb2lkICQoXGJhcnt4fSwgXGJhcnt5fSkkIGFsd2F5cyBsaWVzIG9uIHRoZSBMU1JMLgoKJCQKYSA9IFxiYXJ7eX0gLSBiKFxiYXJ7eH0pCiQkCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMjIElkZW50aWZ5IHRoZSBjb2VmZmljaWVudHMgaW4gcmVncmVzc2lvbiBvdXRwdXQKCmBgYHtyfQojIE5vdGUgZGlmZmVyZW50IHN5bnRheCB3aXRoIGRhdGEgPSAuLi4KbW9kZWwgPC0gbG0odG90YWxHaXZpbmdBbW91bnQgfiBsYXJnZXN0R2lmdEFtb3VudCwgZGF0YSA9IHNhbXBsZURvbm9ycykKI3RvdGFsR2l2aW5nIEFtb3VudCAocmVzcG9uc2UpIChYKSB+IGxhcmdlc3QgZXhwbGFuYXRvcnkgKHkpCm1vZGVsCmBgYAoKYGBge3J9CnN1bW1hcnkobW9kZWwpCmBgYAoKSWRlbnRpZnkgdGhlIHNsb3BlIGFuZCB0aGUgeS1pbnRlcmNlcHQgaW4gdGhlIHJlZ3Jlc3Npb24gb3V0cHV0IGFib3ZlLgoKYGBge3J9CmBgYAoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIC0tLQoKIyBJbnRlcnByZXRpbmcgdGhlIENvZWZmaWNpZW50cyBvZiBhIExpbmVhciBNb2RlbAoKYGBge3J9Cm1vZGVsIDwtIGxtKHRvdGFsR2l2aW5nQW1vdW50IH4gbGFyZ2VzdEdpZnRBbW91bnQsIGRhdGEgPSBzYW1wbGVEb25vcnMpCgptb2RlbAoKIyMjIE5PVEU6IAojIyMgbW9kZWwgPC0gbG0oc2FtcGxlRG9ub3JzJHRvdGFsR2l2aW5nQW1vdW50IH4gc2FtcGxlRG9ub3JzJGxhcmdlc3RHaWZ0QW1vdW50KSAKIyMjIGFsc28gd29ya3MgaGVyZSBidXQgY3JlYXRlcyBhbiBpc3N1ZSB3aXRoIHByZWRpY3QoKSBiZWxvdwoKIyBTdG9yZSB0aGUgY29lZmZpY2llbnRzIG9uIHRoZSBsaW5lYXIgbW9kZWwKbXlfaW50ZXJjZXB0IDwtIHJvdW5kKG1vZGVsJGNvZWZmaWNpZW50c1sxXSwgMSkKcHJpbnQoIkludGVyY2VwdCIpCm15X3Nsb3BlIDwtIHJvdW5kKG1vZGVsJGNvZWZmaWNpZW50c1syXSwgMSkKcHJpbnQoIlNsb3BlIikKIyBQbG90IHNjYXR0ZXJwbG90CnBsb3Qoc2FtcGxlRG9ub3JzJGxhcmdlc3RHaWZ0QW1vdW50LCAKICAgICBzYW1wbGVEb25vcnMkdG90YWxHaXZpbmdBbW91bnQsIAogICAgIGNleCA9IDEuNSwKICAgICBsYXMgPSAxLAogICAgIHBjaCA9IDE2LCAKICAgICB4bGFiID0gIkxhcmdlc3QgR2lmdCBBbW91bnQiLCAKICAgICB5bGFiID0gIlRvdGFsIEdpdmluZyIsCiAgICAgbWFpbiA9IHBhc3RlKCJMU1JMOiAiLCAiUHJlZGljdGVkIHkgPSAiLCBteV9pbnRlcmNlcHQsICIrIiwgbXlfc2xvcGUsICIoeCkiKSkKCiMgUGxvdCBMU1JMCmFibGluZShtb2RlbCwgCiAgICAgICBsd2QgPSAyLCAgICAgICAgIyBtYWtlIHRoZSBsaW5lIHdpZGVyCiAgICAgICBjb2wgPSAicmVkIiAgICAgIyBjb2xvciB0aGUgbGluZQogICAgICAgKQpgYGAKClRoZSAqKnNsb3BlKiogaXMgYSByYXRlIG9mIGNoYW5nZS4KCiQkCnNsb3BlID0gXGZyYWN7cmlzZX17cnVufSA9IFxmcmFje1xEZWx0YSB5fXtcRGVsdGEgeH0gPSBcZnJhY3t5XzIteV8xfXt4XzIteF8xfSA9IFxmcmFje3Nsb3BlfXsxfQokJAoKVG8gaW50ZXJwcmV0IHRoZSBzbG9wZToKCi0gICAqU2xvcGUqOiBXZSBwcmVkaWN0IFxfXF9cXyBbcmVzcG9uc2UgdmFyaWFibGVdIHRvIGluY3JlYXNlIGJ5IFxfXF9cXwogICAgW3Nsb3BlXSBmb3IgZXZlcnkgMSBhZGRpdGlvbmFsIFxfXF9cXyBbZXhwbGFuYXRvcnkgdmFyaWFibGVdLgotICAgRm9yIGEgb25lIHVuaXQgaW5jcmVhc2UgaW4gWCwgd2UgZXhwZWN0IGEgJGJfMSQgY2hhbmdlIGluIFkuCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiQkClktaW50ZXJjZXB0OiAoMCwgeSkKJCQKClRvIGludGVycHJldCB0aGUgeS1pbnRlcmNlcHQ6CgotICAgKlktSW50ZXJjZXB0KjogV2UgcHJlZGljdCBcX1xfXF8gW3ktaW50XSBcX1xfXF8gW3Jlc3BvbnNlIHZhcmlhYmxlXQogICAgd2hlbiBcX1xfXF8gW2V4cGxhbmF0b3J5IHZhcmlhYmxlXSBpcyB6ZXJvLgoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgpJbnRlcnByZXQgdGhlIHNsb3BlIGFuZCB5LWludGVyY2VwdCBvZiBvdXIgbGluZWFyIG1vZGVsOgoKYGBge3J9Cm1vZGVsJGNvZWZmaWNpZW50cwpgYGAKClNsb3BlOiBXZSBwcmVkaWN0IHRoZSB0b3RhbCBnaXZpbmcgdG8gaW5jcmVhc2UgXCQzLjkxIGZvciBlYWNoCmFkZGl0aW9uYWwgXCQxIGluY3JlYXNlIGluIGxhcmdlc3QgZ2lmdCBhbW91bnQuCgpZLWludGVyY2VwdDogV2hlbiBhIGRvbm9yJ3MgbGFyZ2VzdCBnaWZ0IGFtb3VudCBpcyBcJDAsIHRoZSBwcmVkaWN0ZWQKdG90YWwgZ2l2aW5nIGlzIFwkNTEuODIuCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMjIyBGaW5hbCBDb21tZW50cwoKIyMjIyAxLiBUaGUgc2xvcGUgaXMgYSBwcmVkaWN0aW9uIG9yIGVzdGltYXRlOyBpdCdzIG5vdCBhIGd1YXJhbnRlZSwgc28gd2UgbXVzdCB1c2UgInByZWRpY3RlZCIgaW4gb3VyIGludGVycHJldGF0aW9uLgoKTk9UIFRISVM6ICJUaGUgdG90YWwgZ2l2aW5nIHdpbGwgaW5jcmVhc2UgYCQzLjkwYCBmb3IgZXZlcnkgYCQxYAppbmNyZWFzZSBpbiBoaWdoZXN0IGdpZnQgYW1vdW50IgoKIyMjIyAyLiBUaGUgeS1pbnRlcmNlcHQgb2Z0ZW4gZG9lcyBub3QgaGF2ZSBhIG1lYW5pbmdmdWwgaW50ZXJwcmV0YXRpb24uCgoiV2UgcHJlZGljdCBhIHJlc3RpbmcgaGVhcnQgcmF0ZSBvZiAtNTcgYnBtIGZvciBhIGJvZHkgd2VpZ2h0IG9mIDAKcG91bmRzLiIKCiMjIyMgMy4gQmV3YXJlIG9mIGV4dHJhcG9sYXRpb24KCk1ha2luZyBhIHByZWRpY3Rpb24gb3V0c2lkZSB0aGUgZG9tYWluIG9mIG91ciBleHBsYW5hdG9yeSB2YXJpYWJsZS4KClVzZSB0aGUgTFNSTCB0byBwcmVkaWN0IHRoZSB0b3RhbCBnaXZpbmcgb2YgYSBkb25vciB3aG9zZSBsYXJnZXN0IGdpdmVuCndhcyBcJDUwMDAuCgpgYGB7cn0KbW9kZWwKCjUxLjgyICsgMy45MSo1MDAwCgpwcmVkaWN0KG1vZGVsLCBkYXRhLmZyYW1lKGxhcmdlc3RHaWZ0QW1vdW50ID0gNTAwMCkpCmBgYAoKYGBge3J9CnBsb3Qoc2FtcGxlRG9ub3JzJGxhcmdlc3RHaWZ0QW1vdW50LCAKICAgICBzYW1wbGVEb25vcnMkdG90YWxHaXZpbmdBbW91bnQsIAogICAgIGNleCA9IDEuNSwKICAgICBsYXMgPSAxLAogICAgIHBjaCA9IDE2LCAKICAgICB4bGFiID0gIkxhcmdlc3QgR2lmdCBBbW91bnQiLCAKICAgICB5bGFiID0gIlRvdGFsIEdpdmluZyIsCiAgICAgbWFpbiA9IHBhc3RlKCJXaGF0IGlzIGV4dHJhcG9sYXRpb24/IikpCgojIFBsb3QgTFNSTAphYmxpbmUobW9kZWwsIAogICAgICAgbHdkID0gMiwgICAgICAgICMgbWFrZSB0aGUgbGluZSB3aWRlcgogICAgICAgY29sID0gInJlZCIgICAgICMgY29sb3IgdGhlIGxpbmUKICAgICAgICkKYGBgCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMgLS0tCgojIFJlc2lkdWFsIFBsb3RzOiBJcyB0aGUgTW9kZWwgQXBwcm9wcmlhdGU/CgojIyBFeGFtcGxlIDEKCmBgYHtyfQpwb3NpdGlvbiA8LSAxOjEwCnJhdGUgPC0gYygyMy41MywgMTQuOTQsIDExLjE5LCA3LjQ3LCA1LjI5LCAzLjgsIDIuNzksIDIuMTEsIDEuNTcsIDEuMTgpCgojIENyZWF0ZSBzY2F0dGVycGxvdApwbG90KHBvc2l0aW9uLCAgICAgICAgICAgICAgICAgICAgIyB2YXJpYWJsZSBvbiB4LWF4aXMKICAgICByYXRlLCAgICAgICAgICAgICAgICAgICAgICAgICMgdmFyaWFibGUgb24geS1heGlzCiAgICAgcGNoID0gMTYsICAgICAgICAgICAgICAgICAgICAjIGZpbGxlZC1pbiBjaXJjbGVzCiAgICAgY2V4ID0gMiwgICAgICAgICAgICAgICAgICAgICAjIGxhcmdlciBjaXJjbGUgc2l6ZQogICAgIGxhcyA9IDEsICAgICAgICAgICAgICAgICAgICAgIyByb3RhdGUgbnVtYmVycyBvbiB5LWF4aXMKICAgICB4bGFiID0gIlBvc2l0aW9uIiwgICAgICAgICAgICMgeC1heGlzIGxhYmVsCiAgICAgeWxhYiA9ICJDbGljay10aHJvdWdoIFJhdGUiKSAjIHktYXhpcyBsYWJlbAoKYWJsaW5lKHJhdGVfbW9kZWwsIGNvbD0icmVkIiwgMikKYGBgCgpgYGB7cn0KIyAxKSBGaW5kIHRoZSBsaW5lYXIgbW9kZWwgZm9yIHBvc2l0aW9uIHZzLiByYXRlCgpyYXRlX21vZGVsIDwtIGxtIChyYXRlIH4gcG9zaXRpb24pCnJhdGVfbW9kZWwKCiMgMikgQ29uc3RydWN0IGEgcmVzaWR1YWwgcGxvdCAodXNlIHBvc2l0aW9uIHZzLiByZXNpZHVhbHMpLgoKcmF0ZV9tb2RlbCRyZXNpZHVhbHMKcGxvdCAocG9zaXRpb24sIHJhdGVfbW9kZWwkcmVzaWR1YWxzLCBwY2g9MTYpCiMgMykgSW5jbHVkZSBhIGhvcml6b250YWwgbGluZSBhdCByZXNpZHVhbCA9IDAuCgphYmxpbmUoaD0wLCBjb2w9InJlZCIpCgpgYGAKCi0gICBJZiB0aGUgcmVzaWR1YWwgcGxvdCBzaG93cyBhIGN1cnZlZCBwYXR0ZXJuIG9yIGEgZmFubmluZyBvdXQvaW4KICAgIHNoYXBlLCB0aGUgbW9kZWwgd2FzIFxfXF9jdXJ2ZWRcX1xfXF9cX1xfLiBub3QgYXBwcm9wcmlhdGUKCi0gICBJZiB0aGUgcmVzaWR1YWwgcGxvdCBzaG93cyByYW5kb20gc2NhdHRlciwgdGhlIG1vZGVsIHdhcwogICAgYXBwcm9wcmlhdGUuCgpUaGUgbW9kZWwgc2hvdWxkIGV4cGxhaW4gdGhlIHBhdHRlcm47IGlmIHRoZXJlJ3MgYSBsZWZ0LW92ZXIgcGF0dGVybiwKdHJ5IGEgZGlmZmVyZW50IG1vZGVsIChlLmcuLCBxdWFkcmF0aWMpCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMjIEV4YW1wbGUgMgoKYGBge3J9Cm1vZGVsIDwtIGxtKHRvdGFsR2l2aW5nQW1vdW50IH4gbGFyZ2VzdEdpZnRBbW91bnQsIGRhdGEgPSBzYW1wbGVEb25vcnMpCgpwbG90KHNhbXBsZURvbm9ycyRsYXJnZXN0R2lmdEFtb3VudCwgCiAgICAgbW9kZWwkcmVzaWR1YWxzLCAKICAgICB4bGFiID0gIkxhcmdlc3QgR2lmdCBBbW91bnQiLCAKICAgICB5bGFiID0gIlJlc2lkdWFsIiwgCiAgICAgbGFzID0gMSwKICAgICBwY2ggPSAxNiwKICAgICBjZXggPSAxLjUpCgphYmxpbmUoaCA9IDAsIGNvbCA9ICJyZWQiLCBsd2QgPSAyKQpgYGAKCiMjIEV4YW1wbGUgMwoKSXMgYSBsaW5lYXIgbW9kZWwgYW4gYXBwcm9wcmlhdGUgbW9kZWwgZm9yIHRoZSByZWxhdGlvbnNoaXBzIGJldHdlZW4gYWdlCmFuZCBsYXJnZXN0IGdpZnQgYW1vdW50PwoKYGBge3J9CnBsb3Qoc2FtcGxlRG9ub3JzJGFnZSwgc2FtcGxlRG9ub3JzJGxhcmdlc3RHaWZ0QW1vdW50KQpgYGAKCmBgYHtyfQojIENyZWF0ZSBsaW5lYXIgbW9kZWwKbW9kZWxfYWdlIDwtIGxtKHNhbXBsZURvbm9ycyRsYXJnZXN0R2lmdEFtb3VudCB+IHNhbXBsZURvbm9ycyRhZ2UpCgojIFBsb3QgbW9kZWwgZGlhZ25vc3RpY3MKcGxvdChtb2RlbF9hZ2UpCmBgYAoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgpGb3IgbW9yZSBvbiByZXNpZHVhbCBwbG90cywgW3NlZQpMU1JdKGh0dHBzOi8vbGVhcm5pbmdzdGF0aXN0aWNzd2l0aHIuY29tL2Jvb2svcmVncmVzc2lvbi5odG1sI3JlZ3Jlc3Npb25saW5lYXJpdHkpLgoKIyAtLS0KCiMgUi1TcXVhcmVkOiBIb3cgV2VsbCBEb2VzIHRoZSBNb2RlbCBGaXQ/CgoqKlJlc2lkdWFsIHBsb3RzOioqIGRpZCBJIHBpY2sgYW4gYXBwcm9wcmlhdGUgbW9kZWw/CgoqKlItU3F1YXJlZDoqKiBkb2VzIHRoZSBtb2RlbCBkbyBhIGdvb2Qgam9iPwoKLSAgIEFwcHJvcHJpYXRlLCBidXQgcG9vciBwZXJmb3JtYW5jZQoKRm9yIG1vcmUgb24gci1zcXVhcmVkLCBbc2VlCkxTUi5dKGh0dHBzOi8vbGVhcm5pbmdzdGF0aXN0aWNzd2l0aHIuY29tL2Jvb2svcmVncmVzc2lvbi5odG1sI3IyKQoKW0NvbXBhcmluZyBTU0Ugd2l0aCBTU1RdKGh0dHBzOi8vd3d3Lmdlb2dlYnJhLm9yZy9tL0R0c01rajJSKQoKYGBge3J9CgojIEhhdmUgYm90aCBwbG90cyBhcHBlYXIgaW4gb25lIHZpZXdpbmcgd2luZG93ICgxIHJvdywgMiBjb2x1bW5zKQpwYXIobWZyb3cgPSBjKDEsIDIpKQoKIyBQbG90IDEKcGxvdChzYW1wbGVEb25vcnMkbGFyZ2VzdEdpZnRBbW91bnQsIAogICAgIHNhbXBsZURvbm9ycyR0b3RhbEdpdmluZ0Ftb3VudCwgCiAgICAgY2V4ID0gMSwKICAgICBsYXMgPSAxLAogICAgIHBjaCA9IDE2LCAKICAgICB4bGFiID0gIkxhcmdlc3QgR2lmdCBBbW91bnQiLCAKICAgICB5bGFiID0gIlRvdGFsIEdpdmluZyIsIAogICAgIG1haW4gPSAiU2hvdWxkIHdlIHVzZSB0aGUgbWVhbj8iKQoKIyBQbG90IG1lYW4gcmVzcG9uc2UKYWJsaW5lKGggPSBtZWFuKHNhbXBsZURvbm9ycyR0b3RhbEdpdmluZ0Ftb3VudCksIGx3ZCA9IDIsIGNvbCA9ICJibHVlIikKCiMgUGxvdCAyCnBsb3Qoc2FtcGxlRG9ub3JzJGxhcmdlc3RHaWZ0QW1vdW50LCAKICAgICBzYW1wbGVEb25vcnMkdG90YWxHaXZpbmdBbW91bnQsIAogICAgIGNleCA9IDEsCiAgICAgbGFzID0gMSwKICAgICBwY2ggPSAxNiwgCiAgICAgeGxhYiA9ICJMYXJnZXN0IEdpZnQgQW1vdW50IiwgCiAgICAgeWxhYiA9ICJUb3RhbCBHaXZpbmciLAogICAgIG1haW4gPSAiU2hvdWxkIHdlIHVzZSB0aGUgbW9kZWw/IikKCiMgUGxvdCBMU1JMCmFibGluZShtb2RlbCwgCiAgICAgICBsd2QgPSAyLCAgICAgICAgIyBtYWtlIHRoZSBsaW5lIHdpZGVyCiAgICAgICBjb2wgPSAicmVkIiAgICAgIyBjb2xvciB0aGUgbGluZQogICAgICAgKQpgYGAKCldoaWNoIG1vZGVsIHNob3VsZCB3ZSB1c2UgdG8gbWFrZSBwcmVkaWN0aW9uczogdGhlIG1lYW4gb2YgdGhlIHJlc3BvbnNlCm9yIHRoZSBMU1JMPwoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIyBDYWxjdWxhdGUgU1NUCgpgYGB7cn0KIyBWaWV3IHJlc3BvbnNlIHZhcmlhYmxlIHZhbHVlcwpzYW1wbGVEb25vcnMkdG90YWxHaXZpbmdBbW91bnQKCiMgRmluZCBkZXZpYXRpb24gZnJvbSBwb2ludCB0byB0aGUgbWVhbiByZXNwb25zZQp0b3RHaXZpbmdEZXYgPC0gc2FtcGxlRG9ub3JzJHRvdGFsR2l2aW5nQW1vdW50IC0gbWVhbihzYW1wbGVEb25vcnMkdG90YWxHaXZpbmdBbW91bnQpCnRvdEdpdmluZ0RldgoKIyBTcXVhcmUgdGhlIGRldmlhdGlvbnMKdG90R2l2aW5nRGV2XjIKCiMgQ2FsY3VsYXRlIHRvdGFsIHN1bSBvZiBzcXVhcmVzCnNzdCA8LSBzdW0odG90R2l2aW5nRGV2XjIpCgpzc3QKCmBgYAoKIyMgQ2FsY3VsYXRlIFNTRQoKYGBge3J9CiMgRmluZCBkZXZpYXRpb24gZnJvbSBwb2ludCB0byBMU1JMCm1vZGVsJHJlc2lkdWFscwoKIyBTcXVhcmUgdGhlIGRldmlhdGlvbnMKKG1vZGVsJHJlc2lkdWFscyleMgoKIyBDYWxjdWxhdGUgc3VtIG9mIHNxdWFyZXMgd2hlbiB1c2luZyB0aGUgbW9kZWwKc3NlIDwtIHN1bSgobW9kZWwkcmVzaWR1YWxzKV4yKQpzc2UKYGBgCgpOb3RlOiBJIHVzZWQgYFNTRWAgaGVyZSwgYnV0IGBTU01gIG9yIGBTU1JgIG9yIGBTU3Jlc2lkYCBhcmUgb3RoZXIgbmFtZXMKZm9yIHRoZSBzYW1lIHF1YW50aXR5LgoKIyMgQ2FsY3VsYXRlIHRoZSBwcm9wb3J0aW9uIG9mIGVycm9yIHRoYXQgcmVtYWlucwoKYGBge3J9CnNzZSAvIHNzdApgYGAKCiMjIENhbGN1bGF0ZSB0aGUgcHJvcG9ydGlvbiBvZiBlcnJvciB0aGF0IHRoZSBMU1JMIHJlbW92ZWQKCmBgYHtyfQoxIC0gc3NlL3NzdApgYGAKCkFuIGFtYXppbmcgY29ubmVjdGlvbjoKCmBgYHtyfQojIEZpbmQgdGhlIGNvcnJlbGF0aW9uCmdpdmluZ19jb3JyZWxhdGlvbiA8LSBjb3Ioc2FtcGxlRG9ub3JzJGxhcmdlc3RHaWZ0QW1vdW50LCBzYW1wbGVEb25vcnMkdG90YWxHaXZpbmdBbW91bnQpCmdpdmluZ19jb3JyZWxhdGlvbgoKIyBTcXVhcmUgdGhlIGNvcnJlbGF0aW9uCmdpdmluZ19jb3JyZWxhdGlvbl4yCgpgYGAKCiQkCnJeMiA9IDEtXGZyYWN7U1NFfXtTU1R9CiQkCgpXZSB3YW50ICRyXjIkIHRvIGJlIGFzIGxhcmdlIGFzIHBvc3NpYmxlIChpLmUuLCBjbG9zZSB0byAxLjAwKTsgd2UgY2FuCmNvbXBhcmUgJHJeMiQgYWNyb3NzIHZhcmlvdXMgbW9kZWxzLgoKIyMgSW50ZXJwcmV0YXRpb24KCiRyXjIkOiAqKiJ0aGUgY29lZmZpY2llbnQgb2YgZGV0ZXJtaW5hdGlvbiIqKgoKLSAgIE1lYXN1cmVzIG9mIHRoZSBwZXJjZW50IHJlZHVjdGlvbiBpbiB0aGUgc3VtIG9mIHNxdWFyZWQgcmVzaWR1YWxzCiAgICB3aGVuIHVzaW5nIHRoZSBMU1JMIHRvIG1ha2UgcHJlZGljdGlvbnMsIHJhdGhlciB0aGFuIHVzaW5nIHRoZSBtZWFuCiAgICB2YWx1ZSBvZiB5ICh0aGUgcmVzcG9uc2UgdmFyaWFibGUpCgotICAgTWVhc3VyZXMgdGhlIHByb3BvcnRpb24gKG9yIHBlcmNlbnRhZ2UpIG9mIHRoZSB2YXJpYXRpb24gaW4gdGhlCiAgICByZXNwb25zZSB2YXJpYWJsZSB0aGF0IGlzIGFjY291bnRlZCBmb3IgYnkgdGhlIExTUkwgdXNpbmcgdGhlCiAgICBleHBsYW5hdG9yeSB2YXJpYWJsZQoKYGBge3J9CiMgUmVncmVzc2lvbiBvdXRwdXQgcHJvdmlkZXMgUi1zcXVhcmVkCnN1bW1hcnkobW9kZWwpCmBgYAoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgpJbnRlcnByZXQgJHJeMiA9IDAuMzQkIGluIHRoaXMgY29udGV4dC4KCi0gICBUaGUgTFNSTCB1c2luZyBsYXJnZXN0IGdpZnQgYW1vdW50IGFjY291bnRzIGZvciAzNCUgb2YgdGhlCiAgICB2YXJpYWJpbGl0eSBpbiB0b3RhbCBnaXZpbmcgYW1vdW50LgoKLSAgIDM0JSBvZiB0aGUgdmFyaWFiaWxpdHkgaW4gdG90YWwgZ2l2aW5nIGFtb3VudCBpcyBhY2NvdW50ZWQgZm9yIGJ5CiAgICB0aGUgTFNSTCB1c2luZyBsYXJnZXN0IGdpZnQgYW1vdW50LgoKVGhlIGxpbmVhciBtb2RlbCB1c2luZyBbKmV4cGxhbmF0b3J5IHZhcmlhYmxlKl0gYWNjb3VudHMgZm9yICRyXjIkIG9mCnRoZSB2YXJpYWJpbGl0eSBpbiBbKnJlc3BvbnNlIHZhcmlhYmxlKl0uCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMjIE9uZSBsYXN0IGV4YW1wbGUKCmBgYHtyfQpwb3NpdGlvbiA8LSAxOjEwCnJhdGUgPC0gYygyMy41MywgMTQuOTQsIDExLjE5LCA3LjQ3LCA1LjI5LCAzLjgsIDIuNzksIDIuMTEsIDEuNTcsIDEuMTgpCgpwYXIobWZyb3cgPSBjKDEsIDIpKQoKcGxvdChwb3NpdGlvbiwgICAgICAgICAgICAgICAgICAgCiAgICAgcmF0ZSwgICAgICAgICAgICAgICAgICAgICAgICAKICAgICBwY2ggPSAxNiwgICAgICAgICAgICAgICAgICAgIAogICAgIGNleCA9IDIsICAgICAgICAgICAgICAgICAgICAgCiAgICAgbGFzID0gMSwgICAgICAgICAgICAgICAgICAgICAKICAgICB4bGFiID0gIlBvc2l0aW9uIiwgICAgICAgICAgIAogICAgIHlsYWIgPSAiQ2xpY2stdGhyb3VnaCBSYXRlIiwKICAgICBtYWluID0gIlNob3VsZCB3ZSB1c2UgdGhlIG1lYW4/IikgCmFibGluZShoID0gbWVhbihyYXRlKSwgbHdkID0gMiwgY29sID0gImJsdWUiKQoKcGxvdChwb3NpdGlvbiwgICAgICAgICAgICAgICAgICAgCiAgICAgcmF0ZSwgICAgICAgICAgICAgICAgICAgICAgICAKICAgICBwY2ggPSAxNiwgICAgICAgICAgICAgICAgICAgIAogICAgIGNleCA9IDIsICAgICAgICAgICAgICAgICAgICAgCiAgICAgbGFzID0gMSwgICAgICAgICAgICAgICAgICAgICAKICAgICB4bGFiID0gIlBvc2l0aW9uIiwgICAgICAgICAgIAogICAgIHlsYWIgPSAiQ2xpY2stdGhyb3VnaCBSYXRlIiwKICAgICBtYWluID0gIlNob3VsZCB3ZSB1c2UgdGhlIG1vZGVsPyIpIAoKYWJsaW5lKGxtKHJhdGV+cG9zaXRpb24pLCBsd2QgPSAyLCBjb2wgPSAicmVkIikKCmBgYAoKYGBge3J9CmNvcihwb3NpdGlvbiwgcmF0ZSleMgpgYGAKCkludGVycHJldCAkcl4yPTAuODE0NCQgaW4gY29udGV4dC4KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyAtLS0KCiMgSW5mZXJlbmNlIGZvciBTbG9wZTogQ0kKClVzZSBhIHN0YXRpc3RpYyBmcm9tIGEgc2FtcGxlIHRvIGxlYXJuIGFib3V0IGFuIHVua25vd24gcGFyYW1ldGVyIGZyb20gYQpwb3B1bGF0aW9uLgoKU3VtbWFyeSBvZiBzb21lIHBhcmFtZXRlcnMgYW5kIHN0YXRpc3RpY3MgaW4gdGhpcyBjb3Vyc2U6Cgp8ICAgfCBEYXRhIFR5cGUgfCBQYXJhbWV0ZXIgfCBTdGF0aXN0aWMgfAp8LS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwKfCBNZWFucyB8IFF1YW50aXRhdGl2ZSB8ICRcbXUkIHwgJFxiYXJ7eH0kIHwKfCBEaWZmZXJlbmNlIGluIE1lYW5zIHwgUXVhbnRpdGF0aXZlIHwgJFxtdV8xLVxtdV8yJCB8ICRcYmFye3h9XzEtXGJhcnt4fV8yJCB8CnwgUHJvcG9ydGlvbiB8IENhdGVnb3JpY2FsIHwgJHAkIHwgJFx3aWRlaGF0e3B9JCB8CnwgRGlmZmVyZW5jZSBpbiBQcm9wb3J0aW9ucyB8IENhdGVnb3JpY2FsIHwgJHBfMS1wXzIkIHwgJFx3aWRlaGF0e3AgfV8xLVx3aWRlaGF0e3B9XzIkIHwKfCBTbG9wZSB8IFF1YW50aXRhdGl2ZSB8ICRcYmV0YSQgfCAkYiQgb3IgJGJfMSQgb3IgJFx3aWRlaGF0e1xiZXRhfV8xJCB8CgoqKkNvbmZpZGVuY2UgSW50ZXJ2YWw6KiogV2Ugd291bGQgbGlrZSB0byBlc3RpbWF0ZSB0aGUgdHJ1ZSBwb3B1bGF0aW9uCnNsb3BlICRcYmV0YSQgYmV0d2VlbiB0d28gdmFyaWFibGVzIHVzaW5nIG91ciBzYW1wbGUgc2xvcGUgJGIkLgoKIyMgU2FtcGxpbmcgVmFyaWFiaWxpdHkgSWxsdXN0cmF0aW9uCgpgYGB7cn0KIyBJIGFtIG5vdCBzZXR0aW5nIGEgcmFuZG9tIHNlZWQgaGVyZSwgc28geW91ciByZXN1bHRzIHdpbGwgZGlmZmVyIGZyb20gbWluZS4KCiMgUGljayA0IGRpZmZlcmVudCByYW5kb20gc2FtcGxlcwpzYW1wbGVJRHMxIDwtIHNhbXBsZSgxOjk1NDEyLCBzaXplID0gNTAsIHJlcGxhY2UgPSBGQUxTRSkKc2FtcGxlSURzMiA8LSBzYW1wbGUoMTo5NTQxMiwgc2l6ZSA9IDUwLCByZXBsYWNlID0gRkFMU0UpCnNhbXBsZUlEczMgPC0gc2FtcGxlKDE6OTU0MTIsIHNpemUgPSA1MCwgcmVwbGFjZSA9IEZBTFNFKQpzYW1wbGVJRHM0IDwtIHNhbXBsZSgxOjk1NDEyLCBzaXplID0gNTAsIHJlcGxhY2UgPSBGQUxTRSkKCiMgU2VsZWN0IHRoZSBjb2x1bW5zIEknbSBpbnRlcmVzdGVkIGluCm15Q29sdW1ucyA8LSBjKCJhZ2UiLCAiaW5jb21lUmF0aW5nIiwgInRvdGFsR2l2aW5nQW1vdW50IiwgImxhcmdlc3RHaWZ0QW1vdW50IiwgImlzSG9tZW93bmVyIiwgImdlbmRlciIsICJ1cmJhbmljaXR5IiwgInJlc3BvbmRlZE1haWxpbmciKQoKIyBTdG9yZSB0aGUgcmFuZG9tbHkgc2VsZWN0ZWQgcm93cyBvbiB0aGUgZ2l2ZW4gY29sdW1ucwpzYW1wbGVEb25vcnMxIDwtIGRvbm9yc1tzYW1wbGVJRHMxLCBteUNvbHVtbnNdCnNhbXBsZURvbm9yczIgPC0gZG9ub3JzW3NhbXBsZUlEczIsIG15Q29sdW1uc10Kc2FtcGxlRG9ub3JzMyA8LSBkb25vcnNbc2FtcGxlSURzMywgbXlDb2x1bW5zXQpzYW1wbGVEb25vcnM0IDwtIGRvbm9yc1tzYW1wbGVJRHM0LCBteUNvbHVtbnNdCgojIENhbGN1bGF0ZSBsaW5lYXIgbW9kZWwgZm9yIGVhY2ggc2FtcGxlCm1vZDEgPC0gbG0oc2FtcGxlRG9ub3JzMSR0b3RhbEdpdmluZ0Ftb3VudH5zYW1wbGVEb25vcnMxJGxhcmdlc3RHaWZ0QW1vdW50KQptb2QyIDwtIGxtKHNhbXBsZURvbm9yczIkdG90YWxHaXZpbmdBbW91bnR+c2FtcGxlRG9ub3JzMiRsYXJnZXN0R2lmdEFtb3VudCkKbW9kMyA8LSBsbShzYW1wbGVEb25vcnMzJHRvdGFsR2l2aW5nQW1vdW50fnNhbXBsZURvbm9yczMkbGFyZ2VzdEdpZnRBbW91bnQpCm1vZDQgPC0gbG0oc2FtcGxlRG9ub3JzNCR0b3RhbEdpdmluZ0Ftb3VudH5zYW1wbGVEb25vcnM0JGxhcmdlc3RHaWZ0QW1vdW50KQoKIyBTdG9yZSB0aGUgc2xvcGUgZnJvbSBlYWNoIG1vZGVsCm1vZDFzbG9wZSA8LSByb3VuZChtb2QxJGNvZWZmaWNpZW50c1syXSwgMSkKbW9kMnNsb3BlIDwtIHJvdW5kKG1vZDIkY29lZmZpY2llbnRzWzJdLCAxKQptb2Qzc2xvcGUgPC0gcm91bmQobW9kMyRjb2VmZmljaWVudHNbMl0sIDEpCm1vZDRzbG9wZSA8LSByb3VuZChtb2Q0JGNvZWZmaWNpZW50c1syXSwgMSkKCiMgU3RvcmUgci1zcXVhcmVkIGZvciBlYWNoIG1vZGVsCm1vZDFjb3JyIDwtIHJvdW5kKHNxcnQoc3VtbWFyeShtb2QxKSRyLnNxdWFyZWQpLCAyKQptb2QyY29yciA8LSByb3VuZChzcXJ0KHN1bW1hcnkobW9kMikkci5zcXVhcmVkKSwgMikKbW9kM2NvcnIgPC0gcm91bmQoc3FydChzdW1tYXJ5KG1vZDMpJHIuc3F1YXJlZCksIDIpCm1vZDRjb3JyIDwtIHJvdW5kKHNxcnQoc3VtbWFyeShtb2Q0KSRyLnNxdWFyZWQpLCAyKQoKIyAyeDIgdmlld2luZyB3aW5kb3cKcGFyKG1mcm93ID0gYygyLCAyKSkKCiMgUGxvdCA0IHNjYXR0ZXJwbG90cyB3aXRoIExTUkwgdG8gaWxsdXN0cmF0ZSBzYW1wbGluZyB2YXJpYWJpbGl0eQoKcGxvdChzYW1wbGVEb25vcnMxJGxhcmdlc3RHaWZ0QW1vdW50LCBzYW1wbGVEb25vcnMxJHRvdGFsR2l2aW5nQW1vdW50LCBjZXggPSAxLjUsIGxhcyA9IDEsIHBjaCA9IDE2LCAgeGxhYiA9ICJMYXJnZXN0IEdpZnQiLCAgeWxhYiA9ICJUb3RhbCBHaXZpbmciLCBtYWluID0gcGFzdGUoImIgPSIsIG1vZDFzbG9wZSwgImFuZCByID0iLCBtb2QxY29ycikpCgphYmxpbmUobW9kMSwgY29sID0gInJlZCIsIGx3ZCA9IDEuNSkKCnBsb3Qoc2FtcGxlRG9ub3JzMiRsYXJnZXN0R2lmdEFtb3VudCwgc2FtcGxlRG9ub3JzMiR0b3RhbEdpdmluZ0Ftb3VudCwgY2V4ID0gMS41LCBsYXMgPSAxLCBwY2ggPSAxNiwgIHhsYWIgPSAiTGFyZ2VzdCBHaWZ0IiwgIHlsYWIgPSAiVG90YWwgR2l2aW5nIiwgbWFpbiA9IHBhc3RlKCJiID0iLCBtb2Qyc2xvcGUsICJhbmQgciA9IiwgbW9kMmNvcnIpKQoKYWJsaW5lKG1vZDIsIGNvbCA9ICJyZWQiLCBsd2QgPSAxLjUpCgpwbG90KHNhbXBsZURvbm9yczMkbGFyZ2VzdEdpZnRBbW91bnQsIHNhbXBsZURvbm9yczMkdG90YWxHaXZpbmdBbW91bnQsIGNleCA9IDEuNSwgbGFzID0gMSwgcGNoID0gMTYsICB4bGFiID0gIkxhcmdlc3QgR2lmdCIsICB5bGFiID0gIlRvdGFsIEdpdmluZyIsIG1haW4gPSBwYXN0ZSgiYiA9IiwgbW9kM3Nsb3BlLCAiYW5kIHIgPSIsIG1vZDNjb3JyKSkKCmFibGluZShtb2QzLCBjb2wgPSAicmVkIiwgbHdkID0gMS41KQoKcGxvdChzYW1wbGVEb25vcnM0JGxhcmdlc3RHaWZ0QW1vdW50LCBzYW1wbGVEb25vcnM0JHRvdGFsR2l2aW5nQW1vdW50LCBjZXggPSAxLjUsIGxhcyA9IDEsIHBjaCA9IDE2LCAgeGxhYiA9ICJMYXJnZXN0IEdpZnQiLCAgeWxhYiA9ICJUb3RhbCBHaXZpbmciLCBtYWluID0gcGFzdGUoImIgPSIsIG1vZDRzbG9wZSwgImFuZCByID0iLCBtb2Q0Y29ycikpCgphYmxpbmUobW9kNCwgY29sID0gInJlZCIsIGx3ZCA9IDEuNSkKCiMgTm90ZSB0byBzZWxmOiBtYWtpbmcgYSBmdW5jdGlvbiB3b3VsZCBoYXZlIGJlZW4gd2lzZSBoZXJlIQpgYGAKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyMgQ29uZmlkZW5jZSBJbnRlcnZhbCBmb3IgdGhlIFNsb3BlIG9mIGEgUG9wdWxhdGlvbiBSZWdyZXNzaW9uIExpbmUKCkVzdGltYXRlICRccG0kIE1hcmdpbiBvZiBFcnJvcgoKQ0kgZm9yIGEgUG9wdWxhdGlvbiBNZWFuJCRcYmFye3h9IFxwbSB0Xipfe2RmfShTRU0pJCQgJCQKYiBccG0gdF4qX3tkZn0oU0VfYikKJCQKCndoZXJlIGIgaXMgdGhlIHNsb3BlLCBgdCpgIGZvbGxvd3MgYSAqdCotZGlzdHJpYnV0aW9uIHdpdGggYGRmID0gbiAtIDJgCmFuZCAkU0VfYiQgaXMgdGhlIHN0YW5kYXJkIGVycm9yIG9mIHRoZSBzbG9wZQoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIyBFeGFtcGxlOiBEb25vciBHaWZ0cwoKYGBge3J9CnBsb3Qoc2FtcGxlRG9ub3JzJGxhcmdlc3RHaWZ0QW1vdW50LCAKICAgICBzYW1wbGVEb25vcnMkdG90YWxHaXZpbmdBbW91bnQsIAogICAgIGNleCA9IDEsCiAgICAgbGFzID0gMSwKICAgICBwY2ggPSAxNiwgCiAgICAgeGxhYiA9ICJMYXJnZXN0IEdpZnQgQW1vdW50IiwgCiAgICAgeWxhYiA9ICJUb3RhbCBHaXZpbmciKQoKIyBQbG90IExTUkwKYWJsaW5lKG1vZGVsLCAKICAgICAgIGx3ZCA9IDIsICAgICAgICAjIG1ha2UgdGhlIGxpbmUgd2lkZXIKICAgICAgIGNvbCA9ICJyZWQiICAgICAjIGNvbG9yIHRoZSBsaW5lCiAgICAgICApCmBgYAoKYGBge3J9CnN1bW1hcnkobW9kZWwpCmBgYAoKIyMjIDEuIENhbGN1bGF0ZSBhIDk1JSBjb25maWRlbmNlIGludGVydmFsIGZvciB0aGUgc2xvcGUgb2YgdGhlIHBvcHVsYXRpb24gcmVncmVzc2lvbiBsaW5lLgoKJCQKYiBccG0gdF4qX3tkZn0oU0VfYikKJCQKCmBgYHtyfQojIHQgY3JpdGljYWwgdmFsdWUKdGNyaXQgPC0gcXQocCA9IDAuOTc1LCBkZiA9IDUwLTIpCnRjcml0CgojIExvd2VyIGJvdW5kCjMuOTEwIC0gdGNyaXQqMC43ODIKCiMgVXBwZXIgYm91bmQKMy45MTAgKyB0Y3JpdCowLjc4MgpgYGAKCiMjIyAyLiBVc2UgUidzIGBjb25maW50KG1vZGVsLCBsZXZlbCA9IC4uLilgIHRvIHZlcmlmeSB5b3VyIGFuc3dlci4KCmBgYHtyfQpjb25maW50KG1vZGVsLCBsZXZlbCA9IDAuOTUpCmBgYAoKIyMjIDMuIFdoeSBtaWdodCBvdXIgaW50ZXJ2YWwgYmUgdW50cnVzdHdvcnRoeT8KCmBgYHtyfQpwbG90KHNhbXBsZURvbm9ycyRsYXJnZXN0R2lmdEFtb3VudCwgCiAgICAgc2FtcGxlRG9ub3JzJHRvdGFsR2l2aW5nQW1vdW50LCAKICAgICBwY2ggPSAxNiwKICAgICBsYXMgPSAxLAogICAgIHhsYWIgPSAiTGFyZ2VzdCBHaWZ0IEFtb3VudCIsCiAgICAgeWxhYiA9ICJUb3RhbCBHaXZpbmciKQpgYGAKCiMjIyA0LiBXZSBrbm93IHRoZSBwb3B1bGF0aW9uIHJlZ3Jlc3Npb24gbGluZSEgKFNlZSBiZWxvdy4pIERpZCBvdXIgaW50ZXJ2YWwgY2FwdHVyZSB0aGUgdHJ1ZSBzbG9wZT8KCmBgYHtyfQojIENhbGN1bGF0ZSBwb3B1bGF0aW9uIHJlZ3Jlc3Npb24gbGluZQpwb3BMaW5lIDwtIGxtKGRvbm9ycyR0b3RhbEdpdmluZ0Ftb3VudCB+IGRvbm9ycyRsYXJnZXN0R2lmdEFtb3VudCkKCiMgUGxvdCBhbGwgZG9ub3JzCnBsb3QoZG9ub3JzJGxhcmdlc3RHaWZ0QW1vdW50LCAKICAgICBkb25vcnMkdG90YWxHaXZpbmdBbW91bnQsIAogICAgIGNleCA9IDEsCiAgICAgbGFzID0gMSwKICAgICBwY2ggPSAxNiwgCiAgICAgeGxhYiA9ICJMYXJnZXN0IEdpZnQgQW1vdW50IiwgCiAgICAgeWxhYiA9ICJUb3RhbCBHaXZpbmciLAogICAgIG1haW4gPSBwYXN0ZSgiUG9wdWxhdGlvbiBTbG9wZSA9ICIsIHJvdW5kKHBvcExpbmUkY29lZmZpY2llbnRzWzJdLCAyKSkpCgojIFBsb3QgUG9wdWxhdGlvbiByZWdyZXNzaW9uIGxpbmUKYWJsaW5lKHBvcExpbmUsIAogICAgICAgbHdkID0gMiwgICAgICAgICMgbWFrZSB0aGUgbGluZSB3aWRlcgogICAgICAgY29sID0gImJsYWNrIiAgICAgIyBjb2xvciB0aGUgbGluZQogICAgICAgKQoKIyBQbG90IExTUkwgZnJvbSBzYW1wbGUgbW9kZWwKYWJsaW5lKG1vZGVsLCAKICAgICAgIGx3ZCA9IDIsICAgICAgICAjIG1ha2UgdGhlIGxpbmUgd2lkZXIKICAgICAgIGNvbCA9ICJyZWQiICAgICAjIGNvbG9yIHRoZSBsaW5lCiAgICAgICApCgojIEFkZCBhIGxlZ2VuZApsZWdlbmQoImJvdHRvbXJpZ2h0IiwgICAgICAgICAgICAgIAogICAgICAgbGVnZW5kID0gYygiUG9wdWxhdGlvbiBMU1JMIiwgIlNhbXBsZSBMU1JMIiksIAogICAgICAgY29sID0gYygiYmxhY2siLCAicmVkIiksICAgCiAgICAgICBwY2ggPSBjKDE1LCAxNSkpIApgYGAKCldlIGFyZSA5NSUgY29uZmlkZW50IHRoYXQgdGhlIGludGVydmFsICgyLjMzNywgNS40ODIpIGNhcHR1cmVzIHRoZQpwb3B1bGF0aW9uIHNsb3BlIG9mIHRvdGFsIGdpdmluZyBvbiBsYXJnZXN0IGdpZnQgYW1vdW50IGZvciBhbGwgdGhlCmRvbm9ycyBpbiB0aGlzIG9yZ2FuaXphdGlvbi4KClRoZSBwb3B1bGF0aW9uIHNsb3BlIHdhcyAyLjY0LCBzbyB5ZXMsIG91ciBpbnRlcnZhbCBjYXB0dXJlZCB0aGUKcGFyYW1ldGVyLgoKIyAtLS0KCiMgSW5mZXJlbmNlIGZvciBTbG9wZTogU1QKCldlIHRvb2sgYSAqKnNhbXBsZSoqIG9mIGRvbm9ycyBmcm9tIHRoZSAqKnBvcHVsYXRpb24qKiBvZiBhbGwgZG9ub3JzLgoKYGBge3J9CiMgRm9yIHJlcHJvZHVjaWJpbGl0eQpzZXQuc2VlZCgxKQoKIyBSYW5kb21seSBzZWxlY3QgNTAgbnVtYmVycyB0byByZXByZXNlbnQgcm93cyBpbiBkYXRhZnJhbWUKc2FtcGxlSURzIDwtIHNhbXBsZSgxOjk1NDEyLCBzaXplID0gNTAsIHJlcGxhY2UgPSBGQUxTRSkKCiMgU2VsZWN0IHRoZSBjb2x1bW5zIEknbSBpbnRlcmVzdGVkIGluCm15Q29sdW1ucyA8LSBjKCJhZ2UiLCAiaW5jb21lUmF0aW5nIiwgInRvdGFsR2l2aW5nQW1vdW50IiwgImxhcmdlc3RHaWZ0QW1vdW50IiwgImlzSG9tZW93bmVyIiwgImdlbmRlciIsICJ1cmJhbmljaXR5IiwgInJlc3BvbmRlZE1haWxpbmciKQoKIyBTdG9yZSB0aGUgcmFuZG9tbHkgc2VsZWN0ZWQgcm93cyBvbiB0aGUgZ2l2ZW4gY29sdW1ucwpzYW1wbGVEb25vcnMgPC0gZG9ub3JzW3NhbXBsZUlEcywgbXlDb2x1bW5zXQpzYW1wbGVEb25vcnMKYGBgCgpXZSB3b3VsZCBsaWtlIHRvIHNlZSBpZiB0aGUgc2FtcGxlIHNsb3BlIGlzICoqc3RhdGlzdGljYWxseQpzaWduaWZpY2FudCoqIChpLmUuLCBhIFxfXF8gUC12YWx1ZSkuCgotICAgQXNzdW1pbmcgdGhlcmUncyBubyBhc3NvY2lhdGlvbiBiZXR3ZWVuIHRoZSB0d28gdmFyaWFibGVzLCBob3cKICAgIGxpa2VseSBpcyBpdCB0aGF0IHdlIHdvdWxkIG9ic2VydmUgYSBzYW1wbGUgc2xvcGUgbGlrZSB0aGlzIG9yIG1vcmUKICAgIGV4dHJlbWUganVzdCBkdWUgdG8gc2FtcGxpbmcgdmFyaWFiaWxpdHk/CgpHb2FsIGZvciBvdXIgbW9kZWxzOiBwcmVkaWN0b3JzIHdpdGggbG93IHAtdmFsdWVzIGFuZCBhIG1vZGVsIHdpdGggYQpoaWdoICRyXjIkCgojIyMgUGVyZm9ybSBhIHRlc3Qgb2Ygc2lnbmlmaWNhbmNlIGZvciB0aGUgcG9wdWxhdGlvbiBzbG9wZSBhdCAkXGFscGhhID0gMC4wNSQuCgokSF8wOiBcYmV0YSA9IDAkCgokSF9hOiBcYmV0YSBcbmUgMCQKCndoZXJlICRcYmV0YSQgPSB0aGUgc2xvcGUgb2YgdGhlIHBvcHVsYXRpb24gcmVncmVzc2lvbiBsaW5lIG9mIHRvdGFsCmdpdmluZyBvbiBsYXJnZXN0IGdpZnQgZm9yIGFsbCBkb25vcnMgaW4gdGhpcyBvcmdhbml6YXRpb24KCmBgYHtyfQpzdW1tYXJ5KG1vZGVsKQpgYGAKClNpbmNlIHRoZSBwLXZhbHVlICoqaXMgbGVzcyB0aGFuKiogdGhlIGFscGhhIGxldmVsIG9mIDAuMDUsIHdlICoqaGF2ZQpjb252aW5jaW5nIGV2aWRlbmNlKiogdGhhdCB0aGUgc2xvcGUgb2YgdGhlIHBvcHVsYXRpb24gcmVncmVzc2lvbiBsaW5lCnVzaW5nIGxhcmdlc3QgZ2lmdCB0byBwcmVkaWN0IHRvdGFsIGdpdmluZyAqKmlzIG5vdCB6ZXJvKiouCgpJbiBvdGhlciB3b3JkcywgdGhlcmUgaXMgY29udmluY2luZyBldmlkZW5jZSBvZiBhbiBhc3NvY2lhdGlvbiBiZXR3ZWVuCmxhcmdlc3QgZ2lmdCBhbW91bnQgYW5kIHRvdGFsIGdpdmluZyBmb3IgKiphbGwqKiBkb25vcnMgaW4gdGhpcwpvcmdhbml6YXRpb24uCgojIyMgQ2FsY3VsYXRlIHQtdGVzdCBzdGF0aXN0aWMgYW5kIHAtdmFsdWUgbWFudWFsbHkKCiQkCnQgPSBcZnJhY3tiXzEtXGJldGF9e1NFX2J9CiQkCgpgYGB7cn0KIyBUZXN0IHN0YXRpc3RpYwoKKDMuOTEwIC0wKSAvIC43ODIKIyBQLXZhbHVlCgoyICogcHQgKHEgPSA1LCBkZiA9IDUwLTIsIGxvd2VyLnRhaWwgPSBGQUxTRSkKYGBgCgpGb3IgbW9yZSBvbiBzaWduaWZpY2FuY2UgdGVzdHMgaW4gcmVncmVzc2lvbiwgW3NlZQpMU1JdKGh0dHBzOi8vbGVhcm5pbmdzdGF0aXN0aWNzd2l0aHIuY29tL2Jvb2svcmVncmVzc2lvbi5odG1sI3JlZ3Jlc3Npb250ZXN0cykuCgojIyMgRmluYWwgTm90ZTogUHJhY3RpY2FsIFNpZ25pZmljYW5jZQoKSnVzdCBiZWNhdXNlIGEgdmFyaWFibGUgaXMgKipzdGF0aXN0aWNhbGx5IHNpZ25pZmljYW50KiogZG9lcyBub3QgbWVhbnMKaXQgaXMgKipwcmFjdGljYWxseSBzaWduaWZpY2FudCEqKgoKLSAgIFN1cHBvc2UgdGhlIG51bWJlciBvZiBhYnNlbmNlcyBwcmVkaWN0cyBncmFkZSBpbiBhIGNvdXJzZSB3aXRoIHRoZQogICAgcC12YWx1ZSA9IDAuMDAwMSBcPCAkXGFscGhhID0gMC4wNSQsIGJ1dCB0aGUgZWZmZWN0IHNpemUgaXMgMC4xJQogICAgbG93ZXIgZ3JhZGUgaW4gdGhlIGNvdXJzZSBmb3IgZWFjaCBhZGRpdGlvbmFsIGFic2VuY2UuIE1vcmUgaGVscGZ1bAogICAgd291bGQgYmUga25vd2luZyBob3cgd2VsbCBkb2VzIHRoZSBtb2RlbCBmaXQsIGkuZS4sIHdoYXQncyAkcl4yJD8KCiMjIyBPbiB5b3VyIG93bgoKQ29sbGVjdCB0d28gcXVhbnRpdGF0aXZlIHZhcmlhYmxlcyBvbiB0aGUgc2FtZSBpbmRpdmlkdWFscyBhbmQgY2FsY3VsYXRlCmEgY29uZmlkZW5jZSBpbnRlcnZhbCBhbmQgcGVyZm9ybSBhIHNpZ25pZmljYW5jZSB0ZXN0IGZvciB0aGUgc2xvcGUuCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMgLS0tCgojIFJlZ3Jlc3Npb24gd2l0aCBDYXRlZ29yaWNhbCBQcmVkaWN0b3JzCgpgYGB7cn0Kc2FtcGxlRG9ub3JzCmBgYAoKIyMgTW9kZWwgd2l0aCBRdWFudGl0YXRpdmUgVmFyaWFibGUKCmBgYHtyfQptb2RlbF9sYXJnZXN0IDwtIGxtKHRvdGFsR2l2aW5nQW1vdW50IH4gbGFyZ2VzdEdpZnRBbW91bnQsIGRhdGEgPSBzYW1wbGVEb25vcnMpCgpzdW1tYXJ5KG1vZGVsX2xhcmdlc3QpCmBgYAoKIyMjIFVzZSBtb2RlbCB0byBtYWtlIHByZWRpY3Rpb24KCmBwcmVkaWN0KG1vZGVsLCBkYXRhZnJhbWUpYAoKYGBge3J9CnByZWRpY3QobW9kZWxfbGFyZ2VzdCwgZGF0YS5mcmFtZShsYXJnZXN0R2lmdEFtb3VudCA9IDUwKSkKYGBgCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMjIE1vZGVsIHdpdGggQ2F0ZWdvcmljYWwgUHJlZGljdG9yCgpgYGB7cn0KbW9kZWxfZ2VuZGVyIDwtIGxtKHRvdGFsR2l2aW5nQW1vdW50IH4gZ2VuZGVyLCBkYXRhID0gc2FtcGxlRG9ub3JzKQoKdGFibGUoc2FtcGxlRG9ub3JzJGdlbmRlcikKCnN1bW1hcnkobW9kZWxfZ2VuZGVyKQpgYGAKCiQkClxoYXR7eX0gPSAxMzkuMTcgKyAyMi4wOChtYWxlKQokJAoKMCA9ICpub3QgbWFsZSogYW5kIDEgPSAqeWVzIG1hbGUqCgojIyMgVXNlIG1vZGVsIHRvIG1ha2UgcHJlZGljdGlvbgoKYGBge3J9CnByZWRpY3QobW9kZWxfZ2VuZGVyLCBkYXRhLmZyYW1lKGdlbmRlciA9ICJtYWxlIikpCgoxMzkuMTcgKyAyMi4wOCoxCmBgYAoKYGBge3J9CnByZWRpY3QobW9kZWxfZ2VuZGVyLCBkYXRhLmZyYW1lKGdlbmRlciA9IGMoIm1hbGUiLCAiZmVtYWxlIikpKQpgYGAKCk5vdGljZSAxMzkuMTcgKyAyMi4wOCgwKSA9IDEzOS4xNywgdGhlIHktaW50ZXJjZXB0CgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMjIEludGVycHJldGluZyBDb2VmZmljaWVudHMgd2l0aCBDYXRlZ29yaWNhbCBQcmVkaWN0b3IKCklmIGEgZG9ub3IgaW4gdGhpcyBzYW1wbGUgaXMgbWFsZSwgd2UgZXhwZWN0IFwkMjIuMDggbW9yZSB0aGFuIGlmIHRoZQpkb25vciBpcyBhIGZlbWFsZS4KCk5vdGUsIGhvd2V2ZXIsIHRoYXQgaW5jbHVkaW5nIGdlbmRlciBpcyBub3Qgc3RhdGlzdGljYWxseSBzaWduaWZpY2FudAooUC12YWx1ZSA9IDAuNjU2NDY1KSwgd2hpY2ggbWVhbnMgdGhlIGRpZmZlcmVuY2Ugd2Ugb2JzZXJ2ZSBiZXR3ZWVuIG1hbGUKYW5kIGZlbWFsZSBkb25vcnMgbWF5IGJlIGR1ZSB0byBjaGFuY2UsIGFuZCBub3QgYW4gYWN0dWFsIGluaGVyZW50CmRpZmZlcmVuY2UgaW4gZ2l2aW5nLgoKIyMjIEEgQ2F0ZWdvcmljYWwgVmFyaWFibGUgd2l0aCBNdWx0aXBsZSBMZXZlbHMKCmBgYHtyfQp0YWJsZShzYW1wbGVEb25vcnMkdXJiYW5pY2l0eSkgCgptb2RlbF91cmJhbmljaXR5IDwtIGxtKHRvdGFsR2l2aW5nQW1vdW50IH4gdXJiYW5pY2l0eSwgZGF0YSA9IHNhbXBsZURvbm9ycykKbW9kZWxfdXJiYW5pY2l0eQpgYGAKCiQkClxoYXR7eX0gPSAxOTkuMjggLSAxMTMuNjMocnVyYWwpICsgMTguNzIoc3VidXJiKSAtIDg0LjQ3KHRvd24pIC0gNjAuNDcodXJiYW4pCiQkCgphXC4gUHJlZGljdCB0aGUgdG90YWwgZ2l2aW5nIGZvciBhIGRvbm9yIGluIHRoaXMgc2FtcGxlIHdobyBsaXZlcyBpbiBhCnJ1cmFsIGFyZWEuCgpgYGB7cn0KMTk5LjI4IC0gMTEzLjYzKigxKQpgYGAKCmJcLiBQcmVkaWN0IHRoZSB0b3RhbCBnaXZpbmcgZm9yIGEgZG9ub3IgaW4gdGhpcyBzYW1wbGUgd2hvIGxpdmVzIGluIHRoZQpzdWJ1cmJzLgoKYGBge3J9CjE5OS4yOCArIDE4LjcyKjEKYGBgCgpjXC4gUHJlZGljdCB0aGUgdG90YWwgZ2l2aW5nIGZvciBhIGRvbm9yIGluIHRoaXMgc2FtcGxlIHdobyBsaXZlcyBpbiBhCmNpdHkgKHdhaXQuLi4pLgoKYGBge3J9CjE5OS4yOCArIDAgKyAwCmBgYAoKQ2l0eSBhcyAicmVmZXJlbmNlIGdyb3VwIgoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIC0tLQoKIyBNdWx0aXBsZSBSZWdyZXNzaW9uCgpDb25zaWRlciB0aGUgZGF0YSBiZWxvdyBmcm9tIGEgcmVjZW50IHllYXIgb24gc2l4LXllYXIgZ3JhZHVhdGlvbiByYXRlCiglKSwgaW5zdHJ1Y3Rpb25hbCBleHBlbmRpdHVyZSBwZXIgZnVsbC10aW1lIHN0dWRlbnQgKGluIGRvbGxhcnMpLCBhbmQKbWVkaWFuIFNBVCBzY29yZSBmb3IgOSBwcmltYXJpbHkgdW5kZXJncmFkdWF0ZSBwdWJsaWMgdW5pdmVyc2l0aWVzIGFuZApjb2xsZWdlcyBpbiB0aGUgd2VzdGVybiBVbml0ZWQgU3RhdGVzIHdpdGggZW5yb2xsbWVudHMgYmV0d2VlbiAxMCwwMDAKYW5kIDIwLDAwMCAoZnJvbSBQZWNrLCBldCBhbC4sICpTdGF0czogTGVhcm5pbmcgZnJvbSBEYXRhKiwgMm5kIGVkLiwKMjM0KQoKYGBge3J9CnJhdGUgPC0gYyg3NSwgNzEuNSwgNTkuMywgNTYuNCwgNTIuNCwgNDgsIDQ1LjgsIDQyLjcsIDQxLjEpCmV4cGVuZCA8LSBjKDY5NjAsIDcyNzQsIDUzNjEsIDUzNzQsIDUwNzAsIDUyMjYsIDU5MjcsIDU2MDAsIDUwNzMpClNBVCA8LSBjKDEyNDIsIDExMTQsIDEwMTQsIDEwNzAsIDkyMCwgODg4LCA5NzAsIDkzNywgODcxKQoKY29sbGVnZXMgPC0gZGF0YS5mcmFtZShyYXRlLCBleHBlbmQsIFNBVCkKY29sbGVnZXMKYGBgCgojIyBDb21wYXJlIHR3byBtb2RlbHMKCmBgYHtyfQojIENyZWF0ZSB0d28gbW9kZWxzLCBwcmVkaWN0aW5nIGdyYWR1YXRpb24gcmF0ZSB3aXRoIGRpZmZlcmVudCBwcmVkaWN0b3JzCm1vZGVsX2V4cGVuZCA8LSBsbShyYXRlIH4gZXhwZW5kKQptb2RlbF9TQVQgPC0gbG0ocmF0ZSB+IFNBVCkKCiMgT3V0cHV0IHRoZSBzdW1tYXJ5IG9mIGVhY2ggbW9kZWwKc3VtbWFyeShtb2RlbF9leHBlbmQpCnN1bW1hcnkobW9kZWxfU0FUKQpgYGAKCmBgYHtyfQojIFByaW50IHItc3F1YXJlZApzdW1tYXJ5KG1vZGVsX2V4cGVuZCkkci5zcXVhcmVkCnN1bW1hcnkobW9kZWxfU0FUKSRyLnNxdWFyZWQKCiMgUHJpbnQgdGhlIHAtdmFsdWUgZm9yIHRoZSBzbG9wZSBvZiBlYWNoIG1vZGVsLCBnaXZlbiBpdCdzIGluIHRoZSAybmQgcm93IGFuZCA0IGNvbHVtbgpzdW1tYXJ5KG1vZGVsX2V4cGVuZCkkY29lZmZpY2llbnRzWzIsIDRdCnN1bW1hcnkobW9kZWxfU0FUKSRjb2VmZmljaWVudHNbMiwgNF0KYGBgCgpDaG9pY2U6CgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCkxpbmVhciBSZWdyZXNzaW9uIE1vZGVsIHdpdGggT25lIFByZWRpY3RvcgoKJCQKXHdpZGVoYXR7eX0gPSBiXzAgKyBiXzEgeF8xIAokJAoKTXVsdGlwbGUgUmVncmVzc2lvbiBNb2RlbCB3aXRoIGBwYCBwcmVkaWN0b3JzOgoKJCQKXHdpZGVoYXR7eX0gPSBiXzAgKyBiXzEgeF8xICsgYl8yIHhfMiArIFxjZG90cyArIGJfcCB4X3AKJCQKClRvIGFzc2VzcyBhIG11bHRpcGxlIHJlZ3Jlc3Npb24gbW9kZWwsIHdlIHVzZSAqKmFkanVzdGVkKiogJHJeMiQsIHdoaWNoCnBlbmFsaXplcyBhIG1vZGVsIHRoYXQgdXNlcyAqdG9vIG1hbnkqICp1c2VsZXNzKiBwcmVkaWN0b3JzLgoKSW4gb3RoZXIgd29yZHMsICoqYWRqdXN0ZWQqKiAkcl4yJCB3aWxsIG9ubHkgaW5jcmVhc2UgaWYgdGhlIG5ldwp2YXJpYWJsZXMgKmltcHJvdmUqIHRoZSBtb2RlbCBwZXJmb3JtYW5jZSBtb3JlIHRoYW4geW91J2QgZXhwZWN0IGJ5CmNoYW5jZS4gRm9yIG1vcmUgaW5mbywgW3NlZSBMU1IKaGVyZV0oaHR0cHM6Ly9sZWFybmluZ3N0YXRpc3RpY3N3aXRoci5jb20vYm9vay9yZWdyZXNzaW9uLmh0bWwjdGhlLWFkanVzdGVkLXIyLXZhbHVlKS4KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyMgUGVyZm9ybSBtdWx0aXBsZSByZWdyZXNzaW9uCgpgbG0oeSB+IHgxKWAKCmBsbSh5IH4geDEgKyB4MiArIHgzICsg4oCmKWAKCmBgYHtyfQojIENyZWF0ZSBtdWx0aXBsZSByZWdyZXNzaW9uCm1vZGVsX211bHQgPC0gbG0ocmF0ZSB+IGV4cGVuZCArIFNBVCkKc3VtbWFyeShtb2RlbF9tdWx0KQpgYGAKCmBgYHtyfQojIFByaW50IHItc3F1YXJlZApwcmludChwYXN0ZShjKAogIHJvdW5kKHN1bW1hcnkobW9kZWxfZXhwZW5kKSRyLnNxdWFyZWQsIDMpLAogIHJvdW5kKHN1bW1hcnkobW9kZWxfU0FUKSRyLnNxdWFyZWQsIDMpLAogIHJvdW5kKHN1bW1hcnkobW9kZWxfbXVsdCkkci5zcXVhcmVkLCAzKSkpKQoKIyBDb21wYXJlIHAtdmFsdWVzIG9uIHRhYmxlCmBgYAoKQ29tbWVudHMKCjEuICBSLXNxdWFyZWQgaW5jcmVhc2VkIHNsaWdodGx5IHRvIDAuODQxMyAodXAgZnJvbSAwLjgzNSkKCjIuICBBZGp1c3RlZCBSLXNxdWFyZWQgZGVjcmVhc2VkIHRvIDAuNzg4NCAoZG93biBmcm9tIDAuODExNCkKCjMuICBUaGUgcC12YWx1ZSBpcyBoaWdoZXIgbm93IGZvciBTQVQuCgo0LiAgVGhlIHAtdmFsdWUgaXMgbm90IHNpZ25pZmljYW50IGZvciBleHBlbmRpdHVyZS4KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyMgSW50ZXJwcmV0aW5nIGEgY29lZmZpY2llbnQgaW4gbXVsdGlwbGUgcmVncmVzc2lvbgoKU0FUID1cPiAwLjA4MSA9XD4gMC4wODElCgotICAgRm9yIGVhY2ggYWRkaXRpb25hbCAxLXBvaW50IGluY3JlYXNlIG9uIG1lZGlhbiBTQVQgc2NvcmUsIHdlIHByZWRpY3QKICAgIHRoZSBncmFkdWF0aW9uIHJhdGUgdG8gaW5jcmVhc2UgMC4wODElLCBob2xkaW5nIGV4cGVuZGl0dXJlCiAgICBjb25zdGFudC4KClVzaW5nIG1vcmUgcmVhbGlzdGljIHVuaXRzOgoKLSAgIEZvciBlYWNoIGFkZGl0aW9uYWwgKioxMDAtcG9pbnQqKiBpbmNyZWFzZSBvbiBtZWRpYW4gU0FUIHNjb3JlLCB3ZQogICAgcHJlZGljdCB0aGUgZ3JhZHVhdGlvbiByYXRlIHRvIGluY3JlYXNlICoqOC4xJSoqLCBob2xkaW5nCiAgICBleHBlbmRpdHVyZSBjb25zdGFudC4KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyMjIE9uIHlvdXIgb3duCgpDcmVhdGUgeW91ciBvd24gbXVsdGlwbGUgcmVncmVzc2lvbiBtb2RlbCAob3IgbW9kZWxzISkgZm9yIGRhdGEgdGhhdAppbnRlcmVzdHMgeW91LiBQdXQgaXQgaW4geW91ciBwb3J0Zm9saW8hCgojIC0tLQoKVGhpcyBtYXRlcmlhbCBpcyBmb3IgZW5yb2xsZWQgc3R1ZGVudHMnIGFjYWRlbWljIHVzZSBvbmx5IGFuZCBwcm90ZWN0ZWQKdW5kZXIgVS5TLiBDb3B5cmlnaHQgTGF3cy4gVGhpcyBjb250ZW50IG11c3Qgbm90IGJlIHNoYXJlZCBvdXRzaWRlIHRoZQpjb25maW5lcyBvZiB0aGlzIGNvdXJzZSwgaW4gbGluZSB3aXRoIEVhc3Rlcm4gVW5pdmVyc2l0eSdzIGFjYWRlbWljCmludGVncml0eSBwb2xpY2llcy4gVW5hdXRob3JpemVkIHJlcHJvZHVjdGlvbiwgZGlzdHJpYnV0aW9uLCBvcgp0cmFuc21pc3Npb24gb2YgdGhpcyBtYXRlcmlhbCwgaW5jbHVkaW5nIGJ1dCBub3QgbGltaXRlZCB0byBwb3N0aW5nIG9uCnRoaXJkLXBhcnR5IHBsYXRmb3JtcyBsaWtlIEdpdEh1YiwgaXMgc3RyaWN0bHkgcHJvaGliaXRlZCBhbmQgbWF5IGxlYWQKdG8gZGlzY2lwbGluYXJ5IGFjdGlvbi4gWW91IG1heSBub3QgYWx0ZXIgb3IgcmVtb3ZlIGFueSBjb3B5cmlnaHQgb3IKb3RoZXIgbm90aWNlIGZyb20gY29waWVzIG9mIGFueSBjb250ZW50IHRha2VuIGZyb20gQnJpZ2h0U3BhY2Ugb3IKRWFzdGVybiBVbml2ZXJzaXR5J3Mgd2Vic2l0ZS4gwqkgQ29weXJpZ2h0IE5vdGljZSAyMDI0LCBFYXN0ZXJuClVuaXZlcnNpdHkgLSBBbGwgUmlnaHRzIFJlc2VydmVkCg==